Lies in education [was Re: The "loop and a half"]

Ben Bacarisse ben.usenet at bsb.me.uk
Thu Oct 12 12:25:16 EDT 2017


Thomas Jollans <tjol at tjol.eu> writes:

> On 2017-10-12 15:16, Ben Bacarisse wrote:
>> Gregory Ewing <greg.ewing at canterbury.ac.nz> writes:
>> 
>>> Ben Bacarisse wrote:
>>>> That's a different type.  I think you mean that a human writing C
>>>> (rather than bartc's code generator) would probably design the code to
>>>> use tokenrec ** then I agree, but the latter is not just a different way
>>>> to write the former.
>>>
>>> Yes, I was translating his English description of the type
>>> into C, using C's meaning of the word "array". It seems that
>>> arrays in the original language (Algol? One of Bart's
>>> inventions?) are somewhat richer things.
>> 
>> Probably, but you can have pointers to array types in C which is what
>> the posted type used.  Humans pass arrays in C by passing a pointer to
>> the first element.  Pointers to arrays therefore crop up when passing 2D
>> (or higher) arrays, even when the code is hand written by a person.
>> 
>
> No, actually. Multi-dimensional arrays in C are not arrays of arrays.

That's exactly what they are (in their simplest form).  Other structures
using pointers can be accessed in the same way so some people call those
multi-dimensional arrays, but that can be a bit confusing.

> They look very similar, but when the dimensions of the array are known
> at compile time, arrays of any rank are a continuous region of memory
> with the data, and a pointer to the start.
>
> Casting int[3][3] to int** will not do what you want it to do.

Exactly.  This is why I raised this as an example where you get a
pointer to an array type.  int ** is something else altogether.

When you pass an int[3][3] to a function, it pops up in the function as
an int (*)[3].  You can (due to another of C's odd rules) write the
parameter as int a[][3], or int a[3][3], but the first array in the
declarator is replaced by a pointer (and the size is ignored).

> If, say, you have variables of the types:
>
> int a[M][N], **b;
>
> then a[i][j] is equivalent to *(a+(i*M)+j),

No it isn't.  That expression does not even have type int.  In fact you
will be surprised to learn that a[i][j] it is equivalent to *(*(a+i)+j).

> while b[i][j] is equivalent to *(*(b+i)+j).

That much is true.  The fact that the genuine 2D array access (a[i][j])
is equivalent an expression of the same form as the pointer to pointer
access (b[i][j]) looks odd because, as you know, they are doing
different things.  But they do different things because the types are
different.

> Observe:
<snip>
>     short multi_array[3][3] = {
>         {1, 0, 0},
>         {0, 1, 0},
>         {0, 0, 1}
>     };
>     short *array_array[3];

I would not have used that name.  This is an array of pointers.

>     /* fill array_array elements of multi_array */
>     for(i=0; i<3; ++i) {
>         array_array[i] = calloc(3, sizeof(short));
>         /* This works because C arrays are row-major */
>         memcpy(array_array[i], &multi_array[i][0], 3*sizeof(short));

Some people would find

     array_array[i] = malloc(sizeof multi_array[i]);
     memcpy(array_array[i], multi_array[i], sizeof multi_array[i]);

to be clearer and more maintainable.  You don't repeat the element type
and there's no need to reference the (possibly arbitrary) size 3.  It
makes it very clear that enough space is being allocated for what is
being copied.

>     }
>
>     /* print out the arrays */
>     puts("multi_array:");
>     for (i=0; i<3; ++i) {
>         for (j=0; j<3; ++j) {
>             printf("%d ", multi_array[i][j]);

Try *(multi_array+(i*3)+j) here to see what happens (that's your
re-write with 'a' and 'M' substituted).

>         }
>         puts("");
>     }

<snip>
-- 
Ben.



More information about the Python-list mailing list