<snip examples which got garbled, look upthread if needed, except>
set_slice (8, (int(*)[8])&bg[2][2]) /* bg[2:5][2:5] */
/* this last case is yucky and if at all frequent
would be handled better by saving an offset pointer or by going through
a flattened pointer as below said:
This, combined with the code sample Netocrat gave here, which I
excerpt:
void print_slice(int fullwidth, int (*ary)[fullwidth], int width, int
height) {
... [snippage]
}
...
int a2d[H][W];
...
print_slice(W, (int(*)[])&a2d[SL_Y][SL_X], SL_W, SL_H);
suggests a new use (in C99) for a type that was largely useless in
C89: the pointer to array of unknown size.
<snip: because compatible with pointer to array of any known size _OR_ VLA
(and same element type), hence convertible w/o cast> <snip: about stride
much more detailed (as usual!) than mine>
Aside: doesn't that locution "the pointer to array of unknown size" sound
like it should be an SF or maybe mystery novel title? <G>
It comes across something like 'The mysterious clue in the boundless
array'. Except a little less tacky.
But this is only one case, arguably an unusual/unlikely one that is not
worth spending much effort on, especially as _this_ case could as easily
have been written with a constant and thus checked size.
Sure, I agree. I just wanted to point out the theoretically possibility.
I would call this no drawback at all; as you say any decent maybe even
halfdecent compiler will optimize it away. I do consider it a very tiny
nuisance to have to think about which identifier -- the actual parameter
or the local copy -- should get the tweaked name, since I don't do this
often enough to have evolved a firm policy.
In those cases I prefer to keep the prototype "pristine" and muddy the
local copy... like putting on your best clothes when you present yourself
to the public and being a little more casual at home.
This also eliminates the constraint Dave Thompson noted (that the
array's second dimension must occur in the argument list before the
array itself), as we can now write:
void f(int (*ap0)[], int fullwidth, int width, int height) { ...
}
}
This is the true and - as far as I can see - only - benefit of removing
the fullwidth dimension from the array parameter. Nevertheless it's a
worthwhile one. I like it.
Concur. I noted the other less palatable ways to do this.
Unfortunately this doesn't extend to arrays of greater than two
dimensions. I rewrote the code for 3d arrays, and it is not possible
according to my reading of the C99 draft to specify a function prototype
as:
void print_3d_slice(int (*aryin)[][], int d2, int d1, int i3, int i2, int i1)
Under section "6.7.5.2 Array declarators" it reads
The element type shall not be an incomplete or function type.
and that would seem to preclude a declaration of the form:
int array[][].
It is possible to write and compile such code under gcc - without warning
in default mode and with a warning in C99 mode. The code works as
expected but it's not standards compliant.
The minimalist left-most placement of the array parameter for an
n-dimensional array is to specify the sizes of the 2nd to n-1th dimension
sizes before the array parameter, and the nth after it.
In the case of three dimensions, this is:
void print_3d_slice(int d2, int (*aryin)[d2][], int d1, int i3, int i2, int i1)
Both of these prototypes require the real type array variable inside
the function and the assignment of the parameter to the inner variable.
Or we can revert to the original syntax and have all dimension sizes
before the array parameter (without requirement for a variable); or some
other variation (in the case of more than 3 dimensions).
The other very useful feature of variable-length arrays when combined with
C90's allowance for mixing declarations and code is that we can create
dynamically allocated multi-dimensional arrays with the same storage
characteristics and syntax access as their statically allocated
counterparts. I'm sure I'm not the first person to comment on this, but
it's something that I think is very useful since I've always considered the
requirement for arrays of pointers to be a waste of space in a traditional
C90 dynamically-allocated 3d array based on the declaration:
int ***dynamic3darray;
Unfortunately it seems that C99 is not likely to
be widely adopted anytime soon according to the mood in clc.
The comments in the following code - which extends the code I first posted
to 3d arrays - illustrate this new type of dynamically allocated array:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
void print_3d_slice_minimal(int d2, int (*aryin)[d2][], int d1, int i3, int i2,
int i1)
void print_3d_slice_gcc(int (*aryin)[][], int d2, int d1, int i3, int i2,
int i1)
*/
void print_3d_slice(int d2, int d1, int (*ary)[d2][d1], int i3, int i2, int i1)
{
int i, j, k;
/* uncomment if using either of the alternative function prototypes
int (*ary)[d2][d1] = aryin;
*/
for (i = 0; i < i3; i++) {
for (j = 0; j < i2; j++) {
for (k = 0; k < i1; k++)
printf("%3d ", ary
[j][k]);
printf("\n");
}
printf("\n");
}
}
int *make_3d_slice(int d2, int d1, int (*ary)[d2][d1], int i3, int i2, int i1)
{
int (*slicep)[i3][i2][i1];
int i, j;
size_t sz1 = sizeof(***slicep);
if (! (slicep = malloc(sizeof(*slicep))) ) {
perror("malloc");
return NULL;
}
/** Could be optimised for the case where i1 == d1 if we make the
* assumption that the passed-in element starts at the beginning of the
* row
*/
for (i = 0; i < i3; i++) {
for (j = 0; j < i2; j++)
memcpy(&(*slicep)[j], &ary[j], sz1);
}
return (int *)slicep;
}
int *alloc_user_3d_array(int *d3, int *d2, int *d1)
{
printf("Enter the 3 sizes of the array dimensions: ");
scanf("%d %d %d", d1, d2, d3);
int (*a3d)[*d2][*d1];
if (!(a3d = malloc(sizeof(***a3d) * *d3 * *d2 * *d1)))
perror("malloc");
return (int *)a3d; /** we can't return the actual type since we can't
specify an array as a return type **/
}
void get_user_slice_dims(int *sl1, int *sl2, int *sl3, int *sl1st, int *sl2st,
int *sl3st)
{
printf("Enter the 3 indices of the slice start: ");
scanf("%d %d %d", sl1st, sl2st, sl3st);
printf("Enter the 3 sizes of the slice dimensions: ");
scanf("%d %d %d", sl1, sl2, sl3);
}
int main(void)
{
int d1, d2, d3, i, j, k, v = 0;
int sl1, sl2, sl3, sl1st, sl2st, sl3st;
int *retval;
/** Here we dynamically allocate a 3d array with size specified by
* user-input. This is only possible in C99; in C90 it is illegal for
* two reasons:
* (1) declaring a3d after the function call is prohibited and
* (2) variable-sized arrays are illegal and a3d is variable-sized
* since the function call can change the variables d2 and d1.
*/
retval = alloc_user_3d_array(&d3, &d2, &d1);
if (!retval)
exit(EXIT_FAILURE);
int (*a3d)[d2][d1] = (int (*)[d2][d1])retval;
for (i = 0; i < d3; i++) {
for (j = 0; j < d2; j++)
for (k = 0; k < d1; k++)
/** We can access a3d as we would any
* statically allocated array, however it is
* dynamically allocated and uses the same
* amount of storage as a statically allocated
* array - the traditional C90 approach would
* be to declare int ***a3d and have to allocate
* two layers of pointers - wasteful of space
* in terms of both memory and source code.
*/
a3d[j][k] = ++v;
}
printf("main array: d3 %d, d2 %d, d1 %d \n", d3, d2, d1);
print_3d_slice(d2, d1, a3d, d3, d2, d1);
get_user_slice_dims(&sl1, &sl2, &sl3, &sl1st, &sl2st, &sl3st);
printf("slice in main array at d3 %d..%d; d2 %d..%d; d1 %d..%d\n",
sl3st, sl3st + sl3 - 1, sl2st, sl2st + sl2 - 1, sl1st,
sl1st + sl1 - 1);
print_3d_slice(d2, d1, (int (*)[d2][d1])&a3d[sl3st][sl2st][sl1st],
sl3, sl2, sl1);
printf("extracting that slice from main array...\n");
/** Again we dynamically allocate a 3d array with the same access syntax
* and storage as a statically allocated array
*/
int (*slice3d)[sl2][sl1];
slice3d = (int (*)[sl2][sl1])make_3d_slice(d2, d1,
(int (*)[d2][d1])&a3d[sl3st][sl2st][sl1st], sl3, sl2, sl1);
if (!slice3d)
exit(EXIT_FAILURE);
printf("extracted slice:\n");
print_3d_slice(sl2, sl1, slice3d, sl3, sl2, sl1);
free(a3d);
free(slice3d);
exit(EXIT_SUCCESS);
}