reference type for C

I

Ian Collins

Keith said:
I've seen no such consensus here. Several people, including myself,
continue to disagree with that assertion.

Maybe those who have taught young children agree with Malcolm and those
who haven't don't?
 
G

glen herrmannsfeldt

James Kuyper said:
On 05/24/2013 08:23 AM, Malcolm McLean wrote:
Some have said that counting is just a more primitive aspect of
arithmetic than addition is, and I think their point of view makes at
least as much sense as yours, but that's not really the key point of
contention. The real issue is your insistence on associating array
subscripting with counting, and not with addition.

Maybe I see this completely wrong, but my understanding is that
is was an analogy, or maybe a disanalogy.

Counting is different than addition, though they are related,
and pointer arithmetic is different than indexing, even
though they are related.

So, yes, indexing is defined in the C standard in terms of pointer
arithmetic, but the implementation doesn't have to work that way
(the hardware might do it for you, without explicit addition)
and many othat languages do indexing without specifying it
in terms of pointer arithmetic.

void add(a,b,n)
double a[], b[];
int *n;
{
for(i=0;i<*n;i++) a += b;
}

subroutine add(a,b,n)
double precision a(*),b(*)
integer n
do i=1,n
a(i)=a(i)+b(i)
end do
end

With a little luck those do exactly the same thing, yet some
would say that one does pointer arithmetic and the other doesn't.

They might even generate exactly the same object code.

-- glen
 
G

glen herrmannsfeldt

gwowen said:
On May 24, 1:39 pm, Eric Sosman <[email protected]>
wrote:
(snip)
It is in some way specific to function calls.
Arrays are passed by reference. To say that "decaying to a pointer"
is not the same as being passed by reference is a distinction without
a difference. That this invisible-conversion-of-an-object-to-
reference occurs in some other contexts isn't really relevant.
(snip)
However you want to phrase it, that's the semantics of
"foo was passed by reference".
Sure you can say "Actually, the address of foo was passed by value",
but on a fundamental level *that's what pass by reference* means.

As long as you don't change the local copy, yes. But you can:

int sum(int a*, int b*, int n) {
int i;
int c[]={3,1,4,1,5,9,2,6};
if(n<=8) b=c;
for(i=0;i<n;i++) a += b;
}

You can't do that with real call by reference.

-- glen

-- glen
 
K

Keith Thompson

Seebs said:
Perhaps most crucially, if my memory of C++ is right, a reference to an
item in an array does not permit array subscripting, precisely because
array subscripting is a pointer-arithmetic kind of thing.
[...]

Actually, it does. A reference acts as an alias for the object to
which it refers. You can even take its address.

const char s[] = "hello";
const char& ref = s[0];
const char* ptr = &ref; // same as &s[0]
// ptr[0] == 'h', ptr[1] == 'e', etc.
 
M

Malcolm McLean

His argument, I think, is that arr[42] is *conceptually* not an
arithmetic operation; rather it refers to the 42nd (well, 43rd)
element of the array arr.

Yes.



And I think I now know why. I kept asking around, and finally found a
reference to that usage. Some teacher-training materials distinguish
between "counting" (1 to N), "counting on" (N to More Than N), and
"arithmetic". So a kid who has learned to get 5 + 3 by saying "five,
six, seven, eight" is "counting on" in this nomenclature.



With that terminology, "eighteen, nineteen, twenty, twenty-one" as a way
of establishing that 18+3 = 21 is "counting on", while doing 8+3, write
down a 1, carry a 1 to the next column, 1+1=2, is "arithmetic".

And then I guess his argument is that "fancy" things like, say,
computing a corresponding location in one string from a location
in another by doing something like:



new_tail = new + (old_tail - old);

is "arithmetic". But simply doing "x = a[42]" isn't, because there's
no need for an actual computation, you can just do it by starting at
a[0] and "counting on".
Seebs has got it.

Counting on in hundreds, tens and ones is actually faster and more accurate
than using the look-up and carry method, and is far easier if you
don't have pencil and paper. You don't need any number facts, beyond
knowing the next one in the list of hundreds, tens, and units. But a lot
of primary schools don't teach it. The reason is that it's a bit of a trap,
it doesn't scale up to huge integers, it can't be extended to decimals easily,
it makes it harder to express ideas like if a + b = c, a = c - b.

There's an important psychological distinction between

ptr = array + i;
and
array = x;

so it's reasonable to call the first "pointer arithmetic" and the
second "array subscripting". But why is that when array = x is syntactical sugar for *(array + i) = x? The reason is that you don't need to understand
the calculation as an addition, you just need to understand it at the
level of counting.
 
E

Eric Sosman

What if I use the name of an array *typename* in prototype of a
function. This is not, then an object decaying to a pointer, but an
entire type being subverted.

As far as I've seen, nobody in this thread has made any
claim that an object decays to a pointer (if anyone had said so,
someone else would surely have stepped up with a correction).
The array-to-pointer conversion applies to "an expression that
has type `array of /type/'" (6.3.2.1p3). An expression is "a
sequence of operators and operands" (6.5p1), while an object is
a "region of data storage" (3.15p1). An expression is not an
object.

But let's take a step back. I've come to believe that your
own mental model of C does in fact incorporate a whole pile of
special cases covering the ways array arguments interact with
function parameters. If the model works for you, well then, it
works. But it seems to me that your model is unsatisfactory, both
more complicated and less expressive than the model everyone else
(including the Standard) appears to use.

Your model requires

- A special case for array arguments, which are passed by
reference while all other function arguments are passed
by value,

- A special case for array parameters, which despite being
"references" somehow lose track of the length of the array
referred to,

- A suspension of disbelief to cover the case where a pointer
(not necessarily an array) is supplied as an argument to a
function with an array parameter,

- An admission that array names usually become pointers, but
a claim that this does not happen in function calls.

Occam's Razor is not infinitely sharp, yet I think it can
slice this baloney.

Still, if that's the way you want to understand C, go ahead.
If you want to understand snowfall as flour sifting down from the
Frost Giants' bakery, go ahead. But I'll stick to my original
opinion: Your model is "misleading" because it mis-leads people
away from a simpler and more powerful explanation, causing them
to expend extra mental energy trying to deal with all those
pointless special cases.
 
K

Keith Thompson

Gareth Owen said:
It has pass-by-value and array-objects-decay-to-pointers.

There is no meaningful sense in which that differs from
arrays-are-passed-by-reference.

Yes, there is; see below.
The main difference is that C has more syntactic sugar for dealing with
arrays.

And that "sugar" means that arrays are, in effect, passed by reference.
From a programmers perspective
"Implicitly-decay-to-pointer-and-pass-pointer-by-value" is the same as
"pass by reference"

Your point is that "decay-to-pointer-and-pass-pointer-by-value" is
technically distinct from pass-by-reference (which from a
language-lawyer perspective it is).

But from a programmers perspective

*there is no observable difference*

Again here's some C++ code

int array[10];

void func(int arr[])
{
// manipulate the contents of array
}

arr is a pointer, not an array.
Tell me how would that code differ in language in which the array was
passed by reference?

If you were writing it in C++, wouldn't that look like

std::vector<int> array(10);

void func(std::vector<int>& arr) // <- Arr passed by reference
{
// EXACTLY THE SAME CODE AS BEFORE
}

[And how might the C++ compiler compile that? Well, it would write push
the address of arr onto the stack as the first argument - i.e. the
compiler would "decay" the vector object into its address, and pass
that address by value -- which is exactly how C defines passing arrays.

If the C++ example is pass-by-reference, then from a programmers
perspective, rather than a standards writer, what makes the C version
different]

C++'s std::vector isn't really an array, though it acts like one in many
ways. C++ has actual arrays (just like C arrays); std::vector is
something else.

Here's an example in C++, based on your snippet above:

#include <vector>
#include <iostream>

std::vector<int> array(10);

void func(std::vector<int>& arr) // <- Arr passed by reference
{
std::cout << arr.size() << "\n";
}

int main() {
func(array);
}

The output is "10". Note that it doesn't lose the size of the vector,
because that information is part of the vector. But again, a vector is
not an array, so that's not a good example.

Ada has actual pass-by-reference for arrays, and does not define
operations on arrays in terms of pointers or pointer arithmetic. A
little background for this: Ada's "String" type is an array type. Ada
uses ":=" for assignment. "&" is the array concatenation operator.
Array indexing uses (), not []. A " character within a string literal
is represented by "", not by \" as in C. Attributes like First, Last,
Length, and Size are introduced by an apostrophe; Foo'Size is like C's
sizeof foo or sizeof (foo). Arrays may have any arbitrary lower bound;
string literals have a lower bound of 1 by default.

with Ada.Text_IO; use Ada.Text_IO;
procedure Arr is

procedure Proc(S: in out String) is
begin
Put_Line("S = """ & S & """");
Put_Line("S'First = " & Integer'Image(S'First) &
", S'Last = " & Integer'Image(S'Last));
S(S'First) := 'H';
end Proc;

Message: String := "hello, world";

begin
Put_Line("Before Proc, Message = """ & Message & """");
Proc(Message);
Put_Line("After Proc, Message = """ & Message & """");
end Arr;

The output is:

Before Proc, Message = "hello, world"
S = "hello, world"
S'First = 1, S'Last = 12
After Proc, Message = "Hello, world"

The difference is that the bounds information for the array parameter
is not lost. The parameter object S *really is* an array. In the
corresponding C program, the parameter *really is* a pointer, nothing
more, nothing less, and if you want to know the length of the array
to whose first element that pointer points, you have to obtain it
by some other means, probably by passing it in via another parameter.

That's the difference between what C does and "passing arrays
by reference". The parameter simply *is not an array*. The thin
layer of syntactic sugar that lets you operate on it (in some ways)
in the same way you'd operate on an array doesn't change that.
And in fact the [] operator requires a pointer operand, not an array
operand (and implicitly requires that that pointer must point to
an element of an array object at run time).

If C actually had pass-by-reference for arrays, in the same manner that
Ada does, then sizeof arr inside the C function func() would give you
the size of the array. It doesn't because arr is a pointer.
 
K

Keith Thompson

Gareth Owen said:
[missing attribution] [...]
class myarr {
public:
int arr_[3];
};

void mutate_by_reference(myarr& x)
{
x.arr_[0] = 1; //
}

void mutate_by_value(myarr x)
{
x.arr_[0] = 1; //
}


Which of those has the same semantics as the C example?

Neither. And rather obviously so.

Only with respect to sizeof(). Which is one of the other major
misfeatures of how the C type system handles arrays...

C's handling of sizeof is entirely consistent. You just have to
understand what it's being applied to.

If you assume that a parameter defined as "int arr[]" is really an
array, then "sizeof arr" is going to be confusing. If you understand
that arr is really a pointer, as the language standard says it is
and as all conforming C compilers implement it, then it's clear what
"sizeof arr" means.

I don't particularly *like* the way C deals with pointers and arrays
(for example, the rule that a parameter declaration like "int arr[]"
is "adjusted" to "int *arr" seems almost deliberately confusing),
but I do understand it. I suggest that understanding how it works
should be a prerequisite for complaining about it.

If you haven't already, read section 6 of the comp.lang.c FAQ,
<http://www.c-faq.com/>.
 
K

Keith Thompson

Gareth Owen said:
What if I use the name of an array *typename* in prototype of a
function. This is not, then an object decaying to a pointer, but an
entire type being subverted.

By "typename", do you mean a name introduced by a typedef?

It's an important distinction. The C standard has a syntactic category
"type-name", and it's not just a single identifier; for example,
"int[42]" is a type-name.
And this subversion of declarations *is* unique to function arguments.

// I declare t to be of type T
int is_t_tsized(T t)
{
// so surely, this must be true, yes?
return (sizeof(T) == sizeof(t));
}

Was the above preceded by something like:

typedef int T[42];

?

No, that equality is not necessarily true. Yes, it's weird and
counterintuitive.
int main()
{
T t;

// Well, this makes sense...
bool always_true = (sizeof(T) == sizeof(t));

// Under what "non-special" circumstances does this return false
bool sometime_false = is_t_the_size_of_a_T(t);

// Under what "non-special" circumstances does this compile?
bool wtf_how_does_this_even_compile = is_t_the_size_of_a_T(&t);
}

Oh yes, that's consistent, reasonable, type-safe and in no way
demonstrative that array types in function arguments are weird.

Not special at all.

I've mostly been discussing what the actual language rules are, not
whether they're weird. I've been describing the way the language is
defined, not defending it as a good idea.

An expression of array type (which includes, but is not limited to, an
identifier that's the name of an array object) is, in most but not all
contexts, implicitly converted to ("decays" to) a pointer to the array's
first element. An argument in a function call is just one of the many
contexts in which this conversion occurs; it is not in any way a special
case.

A parameter declared with an array type, even via a typedef (that's a
good point, BTW), is "adjusted" to a pointer type. This is not a
conversion, it's a compile-time adjustment; as a parameter declaration,
"int arr[]" really *means* "int *arr*. This is a distinct rule from the
decay rule above; the language could have had either rule without the
other.

The [] operator requires a pointer operand and an integer operand. (The
pointer operand is often the result of the implicit conversion of an
array name.) It requires *at run time* that the pointer must point to
an element of an array, with a single object being treated as an array
of one element.

That's the way the language is defined, and if your mental model of the
language does not incorporate the above rules, then *either* you're
going to reach incorrect conclusions about the semantics of some C
programs ("incorrect" meaning inconsistent both with the C standard and
with the behavior of real-world compilers), *or* your mental model will
have to include a large number of special cases, making it much more
confusing than the model presented in the standard.

In the mental model I use, expressions of array type and parameters
of array type are special cases (and I can point to the paragraphs
in the standard that rigorously describe those special cases).
Arrays as function arguments and sizeof applied to function
parameters are not special cases.

My mental model works. Does yours?

Let me be clear: I *do not like* the way C handles pointers and arrays.
I particularly dislike the adjustment rule for function parameters,
especially the fact that in a parameter declaration like "int arr[42]",
the "42" is silently ignored.

Taken together, these rules mean that a program like this:

#include <stdio.h>
#include <ctype.h>

void capitalize(char s[]) {
s[0] = toupper(s[0]);
}

int main(void) {
char message[] = "hello, world";
capitalize(message);
puts(message);
}

works correctly, and *can* be "understood" via a mental model that
doesn't recognize the decay of array expressions, the adjustment of
array parameter declarations, or the fact that the [] operator takes a
pointer operand. And that's my biggest problem with the way C handles
arrays and pointers: it can lead learners down a false path.

There are historical reasons for it. In early pre-ANSI (and in fact
pre-K&R1) C, "int p[];" actually defined p as a pointer. Here's a code
snippet from an early version of Dennis Ritchie's C compiler, dated
1972:

int np[], i;
/* ... */
np = lookup();
*np++ = 1;
*np = t;

That's obviously not valid in modern C, but the language's current
handling of arrays and pointers had to evolve from that earlier version
of the language (and from its predecessors B and BCPL) without breaking
too much existing code.
 
I

Ian Collins

Gareth said:
It has pass-by-value and array-objects-decay-to-pointers.

There is no meaningful sense in which that differs from
arrays-are-passed-by-reference.


And that "sugar" means that arrays are, in effect, passed by reference.
From a programmers perspective
"Implicitly-decay-to-pointer-and-pass-pointer-by-value" is the same as
"pass by reference"

Your point is that "decay-to-pointer-and-pass-pointer-by-value" is
technically distinct from pass-by-reference (which from a
language-lawyer perspective it is).

If you consider an array as a another type, and declare it as such, they
aren't any different from other types:

typedef char Array[10];

void foo( Array a ); // pass an array by value

void bar( Array& a ); // pass an array by reference

In this case, "passing by reference" in C is clearly passing by pointer:

void bar( Array* a );

Which has the disadvantage of messy array access syntax in the function.

The functions foo( char* a ) or foo( char a[] ) aren't functions with an
array as their parameter, they are functions with a pointer as their
parameter.
 
M

Malcolm McLean

In the mental model I use, expressions of array type and parameters
of array type are special cases (and I can point to the paragraphs
in the standard that rigorously describe those special cases).

Arrays as function arguments and sizeof applied to function
parameters are not special cases.
In my mental model pointer arguments to functions, which are of
basic types, are "arrays", except in the relatively unusual case
of a "return pointer", when a function needs to return two values.

Structure arguments can be arrays or singletons. You've just
got to know.

int array[100];

isn't "declaring an array", it's reserving a buffer on the stack.

int *array = malloc(100 * sizeof(int));

is doing exactly the same thing, except the buffer is reserved on
the heap.
You can toggle between the two. So you never use sizeof(array). It's
a nasty little quirk that's liable to cause things to break.

int array[9] = {-2, -1, -2, -1, 0, 1, 2, 1, 2};

is initialising data. Virtually always trivial data like that
should be static. Bigger datasets are usually automatically generated, and
they don't fit well with the normal programming paradigm, which is
that code does thing to externally supplied data.
 
K

Keith Thompson

Ian Collins said:
If you consider an array as a another type, and declare it as such, they
aren't any different from other types:

Not quite. They're still arrays.
typedef char Array[10];

void foo( Array a ); // pass an array by value

No, that's equivalent to

void foo(char a[10]);

which in turn is equivalent to

void foo(char *a);

To demonstrate:

#include <stdio.h>

typedef int t[1000];

void func(t param) {
printf("sizeof (t) = %zu\n", sizeof (t));
printf("sizeof param = %zu\n", sizeof param);
}

int main(void) {
t obj;
func(obj);
}

The output I get is:

sizeof (t) = 4000
sizeof param = 8
void bar( Array& a ); // pass an array by reference

That, of course, is C++, not C. Apparently it does let you pass an
array by reference; given your typedef, sizeof a inside bar() yields 10
* sizeof (int). It's similar, but not identical, to defining a
parameter of type Array*, which is a pointer to an array of 10 ints.
In this case, "passing by reference" in C is clearly passing by pointer:

void bar( Array* a );

Which has the disadvantage of messy array access syntax in the function.

The functions foo( char* a ) or foo( char a[] ) aren't functions with an
array as their parameter, they are functions with a pointer as their
parameter.

Yes.
 
K

Keith Thompson

Malcolm McLean said:
In my mental model pointer arguments to functions, which are of
basic types, are "arrays", except in the relatively unusual case
of a "return pointer", when a function needs to return two values.

Then your mental model is inconsistent with the definition of the
language, unless you add so many special cases to it that it becomes
far more confusing than the model described in the standard.
Structure arguments can be arrays or singletons. You've just
got to know.

How did we get to structures? So far the examples have referred to
ints, arrays of ints, and pointers to ints. Did you mean pointer
arguments?
int array[100];

isn't "declaring an array", it's reserving a buffer on the stack.

It's declaring an array. More precisely, it's defining an
array object. Where that object is allocated depends on where
the declaration appears and whether the declaration includes the
"static" keyword. (You can think of the place automatic objects
are allocated as "the stack", though the standard doesn't use that
term; in particular, there's no guarantee, and usually no benefit
in assuming, that "the stack" grows linearly in memory.)
int *array = malloc(100 * sizeof(int));

is doing exactly the same thing, except the buffer is reserved on
the heap.

It's doing something very different. It's defining a pointer object,
and initializing it with the result of malloc() (which could fail).

It happens, because of the "decay" rule, that you can use the same
notation in both cases to refer to elements of the array. That's
because the [] operator takes a pointer argument, and an array name
decays to a pointer. (No, the standard doesn't use the word "decay",
but it's a common and reasonably unambiguous term for this particular
conversion.)
You can toggle between the two. So you never use sizeof(array). It's
a nasty little quirk that's liable to cause things to break.

sizeof is a fundamental feature of the language whose behavior is
rigorously defined. If you don't understand that, you can't make
full use of the language, and you're likely to make serious mistakes
(that the compiler may or may not be able to detect) if you try.

It's unfortunate, IMHO, that the language's syntax actually
*encourages* the incorrect mental model that you seem to have.
The only real solution is to understand the underlying concepts
of arrays and pointers *and* to understand how the language syntax
expresses those concepts, often in counterintuitive ways.
int array[9] = {-2, -1, -2, -1, 0, 1, 2, 1, 2};

is initialising data. Virtually always trivial data like that
should be static. Bigger datasets are usually automatically generated, and
they don't fit well with the normal programming paradigm, which is
that code does thing to externally supplied data.

Whether it should be static depends on what you're going to be doing
with it. If you're not going to change any of the values, it should
also be const. (And you can, if you like, omit the "9" and let the size
be determined by the initializer.)
 
I

Ian Collins

Keith said:
Ian Collins said:
If you consider an array as a another type, and declare it as such, they
aren't any different from other types:

Not quite. They're still arrays.
typedef char Array[10];

void foo( Array a ); // pass an array by value

No, that's equivalent to

void foo(char a[10]);

which in turn is equivalent to

void foo(char *a);

Yes, you are correct. I managed to confuse myself! Too much C++...
To demonstrate:

#include <stdio.h>

typedef int t[1000];

void func(t param) {
printf("sizeof (t) = %zu\n", sizeof (t));
printf("sizeof param = %zu\n", sizeof param);
}

int main(void) {
t obj;
func(obj);
}

The output I get is:

sizeof (t) = 4000
sizeof param = 8
void bar( Array& a ); // pass an array by reference

That, of course, is C++, not C. Apparently it does let you pass an
array by reference; given your typedef, sizeof a inside bar() yields 10
* sizeof (int). It's similar, but not identical, to defining a
parameter of type Array*, which is a pointer to an array of 10 ints.

I agree. My point was the pass by reference syntax dose add value for
arrays. You get the benefit of type checking without the cost of messy
deference syntax with passing Array*.
 
M

Malcolm McLean

Then your mental model is inconsistent with the definition of the
language, unless you add so many special cases to it that it becomes
far more confusing than the model described in the standard.
It's consistent with a subset of the language. And it's better to
keep to that subset.
int array[100];
isn't "declaring an array", it's reserving a buffer on the stack.

It's declaring an array. More precisely, it's defining an
array object. Where that object is allocated depends on where
the declaration appears and whether the declaration includes the
"static" keyword. (You can think of the place automatic objects
are allocated as "the stack", though the standard doesn't use that
term; in particular, there's no guarantee, and usually no benefit
in assuming, that "the stack" grows linearly in memory.)
If it's not automatic then the buffer is not on the stack, fair
enough. We don't need to worry about implementations which don't use
a stack. We're talking about mental models.
The "array" is unlikely to be 100 values. Probably we're expecting ten,
very occasionally it might go up to twenty, fifty is the maximum anyone
could need, and a hundred is just insane. E.g. it could be a value for
every year someone has worked for a company. The buffer is 100 for
safety. But the actual array will be ten values that we read from
somewhere, representing days worked or something.
It's doing something very different. It's defining a pointer object,
and initializing it with the result of malloc() (which could fail).

It happens, because of the "decay" rule, that you can use the same
notation in both cases to refer to elements of the array.
So the mental model works fine. They're both just buffers.
 
K

Keith Thompson

Malcolm McLean said:
It's consistent with a subset of the language. And it's better to
keep to that subset.

Then you have to know the boundaries of that subset (or of an even
smaller subset of that subset) to avoid going beyond them. I don't see
how you can know the boundaries of the subset without understanding the
difference between your mental model and the one described in the
standard.

Nor do I know why any serious C programmer would be satisfied with not
understanding the language.
int array[100];
isn't "declaring an array", it's reserving a buffer on the stack.

It's declaring an array. More precisely, it's defining an
array object. Where that object is allocated depends on where
the declaration appears and whether the declaration includes the
"static" keyword. (You can think of the place automatic objects
are allocated as "the stack", though the standard doesn't use that
term; in particular, there's no guarantee, and usually no benefit
in assuming, that "the stack" grows linearly in memory.)
If it's not automatic then the buffer is not on the stack, fair
enough. We don't need to worry about implementations which don't use
a stack. We're talking about mental models.
The "array" is unlikely to be 100 values. Probably we're expecting ten,
very occasionally it might go up to twenty, fifty is the maximum anyone
could need, and a hundred is just insane. E.g. it could be a value for
every year someone has worked for a company. The buffer is 100 for
safety. But the actual array will be ten values that we read from
somewhere, representing days worked or something.

I don't understand that at all. Are you saying that a fixed-size array
isn't likely to be as long as 100 elements, and is more likely to be
allocated via malloc with a size based on the program's input?

A lookup table indexed by 8-bit characters will have 256 elements.
Input buffers are commonly 1024 or more characters. Fixed-size data for
some common algorithms can be much bigger than that.

In any case, you claim that
int array[100];
"isn't declaring an array" is quite simply false.
So the mental model works fine. They're both just buffers.

Your mental model "works fine" in this some limited cases. It breaks
down as soon as you try to do anything more complex.

Mine doesn't.
 
S

Seebs

Seebs said:
Perhaps most crucially, if my memory of C++ is right, a reference to an
item in an array does not permit array subscripting, precisely because
array subscripting is a pointer-arithmetic kind of thing. [...]

Actually, it does. A reference acts as an alias for the object to
which it refers. You can even take its address.
const char s[] = "hello";
const char& ref = s[0];
const char* ptr = &ref; // same as &s[0]
// ptr[0] == 'h', ptr[1] == 'e', etc.

Hmm. I am not sure now what I was thinking. Either I was thinking you
couldn't do that (in which case I was wrong), or I was trying to express
just "you can't do pointer arithmetic on the reference in the same way
that you could on a pointer to the object" -- which doesn't exclude the
possibility of taking its address later and doing the arithmetic on that.

-s
 
S

Seebs

Counting on in hundreds, tens and ones is actually faster and more accurate
than using the look-up and carry method, and is far easier if you
don't have pencil and paper.

It may be, but:

I still strongly urge you to NEVER AGAIN use that term for it, because
it's an extremely specialized term from a field the vast majority of
people aren't working in. Heck, I know a number of teachers, and only one
of them has *ever* heard that term.

Meanwhile, "counting on" has a radically different meaning which is familiar
to every English speaker I know over the age of eight or so.
There's an important psychological distinction between
ptr = array + i;
and
array = x;


I'm not sure there is such a distinction. I *am* sure that the distinction
there is basically unrelated, at least in my experience, to the distinction
between arithmetic and what you call "counting on".
so it's reasonable to call the first "pointer arithmetic" and the
second "array subscripting".

I don't think that's necessarily reasonable -- especially when you're talking
about a language in which array subscripting *is* pointer arithmetic, by
definition.
But why is that when array = x is syntactical sugar for *(array + i) = x?
The reason is that you don't need to understand
the calculation as an addition, you just need to understand it at the
level of counting.


It's not a bit like counting, to me. I agree that my mental map of array
subscripting isn't very much like "addition", but it's even *less* like
"counting".

Counting is by nature an iterative process which passes through all the
intermediate steps. Array subscripting is the exact opposite; it is a process
which is non-iterative and skips over any intermediate steps.

Counting from 1 to 42 implies hitting 2, 3, 4, and so on. By contrast,
using something like array[42] implies NOT hitting array[1], array[2],
and so on.

-s
 
S

Seebs

What is this "strict meaning" that you refer to?

Uh. Look! A three-headed monkey!

*throws smoke bomb*

*runs away*
But I don't think the C standard ever defines the term "arithmetic" itself.

Huh. That is disturbing, because without a definition of the term, either
as an adjective or a noun, we run into some strange ambiguities.
If the standard had bothered to describe a group of operators as the
"Binary arithmetic operators", I could use that to justify identifying
"pointer arithmetic" with the use of those operators on operands of
pointer type, which would match my own personal definition of "pointer
arithmetic". But the standard doesn't define any such group.

That is a fascinating point. I don't think I have a C11 handy yet,
so I'm just browsing C99. In 6.5.6 (additive operators), there's an
example assering that "pointer arithmetic" is well defined with pointers
to variable length array types. There's also a reference to pointer
arithmetic in Footnote 91.

Which is interesting, because that means that in C99, so far as I
can tell, the phrase "pointer arithmetic" never occurs in normative
text (!). The index has "arithmetic, pointer" pointed to 6.5.6.
Although!

The index has: "arithmetic operators", with a subcategory of "additive",
6.5.6. Bitwise, multiplicative, and shift operators don't apply to
pointers. Some unary operators do (*, &), but others (+, -) don't.
Increment and decrement are given as a distinct category, but are really
just special cases of the additive operators, mostly.

That said, I would still argue that, even if the term isn't given its
own special definition, the formal definition of array subscripting
is in terms of addition, and the additive operators are, at the very
least, in the index under the heading "arithmetic operators". I note
also that the example of pointer arithmetic given in 6.5.6 uses array
subscripting.

So, three-headed monkey or no, it seems that (1) I was wrong as to whether
there was actually an explicit definition, but (2) I nonetheless assert
that the proposed distinction between "pointer arithmetic" and "array
subscripting" based on how a particular pedagogical approach to teaching
young children addition does not mesh well with the formal language spec.

-s
 
S

Seebs

Maybe those who have taught young children agree with Malcolm and those
who haven't don't?

I doubt it's that simple. Not all people who teach young children use
that particular model. I'd never encountered it, and I'm nearly certain
that I was at one point a young child, and that I eventually learned some
sort of arithmetic.

-s
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top