# variable length array

Discussion in 'C Programming' started by Jean-Claude Arbaut, Jun 3, 2009.

1. ### Jean-Claude ArbautGuest

Hi,

I can use:

double get(int n, int p, double t[n][p], int i, int j) {
return t[j];
}

and in another function
double (*u)[];
t = (double (*)[])calloc(n*p, sizeof(double));
a=get(n,p,u,i,j);

Now, how would I declare u for 3 dimensions or more ?

Jean-Claude Arbaut, Jun 3, 2009

2. ### Barry SchwarzGuest

On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
<> wrote:

>Hi,
>
>I can use:
>
>double get(int n, int p, double t[n][p], int i, int j) {
> return t[j];
>}
>
>and in another function
>double (*u)[];

I believe this would need to be
double (*u)[p];
as in examples 3 and 4 in 6.7.5.2(9)-(10)

>t = (double (*)[])calloc(n*p, sizeof(double));

Surely you meant u here, not t. The cast is unnecessary. Furthermore,
all bits zero need not be the representation of 0.0. A frequently
recommended construct is
t = calloc(n, sizeof *t);
or the malloc "almost equivalent"
t malloc (n * sizeof *t);

>a=get(n,p,u,i,j);
>
>Now, how would I declare u for 3 dimensions or more ?

When declaring arrays in functions and pointers to arrays, only the
high-order dimension can be unspecified/omitted.

If t3 is going to be defined in the function as
double t3[x][y][z]
then u3 will need to be defined as
double (*u3)[y][z];

--
Remove del for email

Barry Schwarz, Jun 3, 2009

3. ### Jean-Claude ArbautGuest

Barry Schwarz wrote:

> On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
> <> wrote:
>
>> Hi,
>>
>> I can use:
>>
>> double get(int n, int p, double t[n][p], int i, int j) {
>> return t[j];
>> }
>>
>> and in another function
>> double (*u)[];

>
> I believe this would need to be
> double (*u)[p];
> as in examples 3 and 4 in 6.7.5.2(9)-(10)

There is one in n1256.pdf, p41
And I'm not a C standard expert, but I tought
it's legal to have one incomplete dimension.

gcc -Wall -std=c99 -pedantic doesn't bark at me.
I know that doesn't prove much, but that's strange.

>> t = (double (*)[])calloc(n*p, sizeof(double));

>
> Surely you meant u here, not t.

yes

> The cast is unnecessary.

Someone just told me that on fr.clc ;-) Actually it's
a bad habit I got from using a bad compiler that gave
a warning for uncasted (mc)alloc. I can't remember
which compiler it was.

> Furthermore,
> all bits zero need not be the representation of 0.0. A frequently
> recommended construct is
> t = calloc(n, sizeof *t);
> or the malloc "almost equivalent"
> t malloc (n * sizeof *t);

Does it guarantee that memory is zeroed ?

>> a=get(n,p,u,i,j);
>>
>> Now, how would I declare u for 3 dimensions or more ?

>
> When declaring arrays in functions and pointers to arrays, only the
> high-order dimension can be unspecified/omitted.

Same remark as above.

> If t3 is going to be defined in the function as
> double t3[x][y][z]
> then u3 will need to be defined as
> double (*u3)[y][z];

Is there really no way to allocate it ?
I mean, it's possible with a warning to do

double *p = malloc(100*sizeof(double));
a = get(10,10,p,0,0);

But what should I do to declare it correctly ?

Jean-Claude Arbaut, Jun 3, 2009
4. ### Keith ThompsonGuest

Jean-Claude Arbaut <> writes:
> Barry Schwarz wrote:
>> On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
>> <> wrote:

[...]
>>> t = (double (*)[])calloc(n*p, sizeof(double));

>>
>> Surely you meant u here, not t.

>
> yes
>
>> The cast is unnecessary.

>
> Someone just told me that on fr.clc ;-) Actually it's
> a bad habit I got from using a bad compiler that gave
> a warning for uncasted (mc)alloc. I can't remember
> which compiler it was.

Could it have been a C++ compiler?

>> Furthermore,
>> all bits zero need not be the representation of 0.0. A frequently
>> recommended construct is
>> t = calloc(n, sizeof *t);
>> or the malloc "almost equivalent"
>> t malloc (n * sizeof *t);

>
> Does it guarantee that memory is zeroed ?

No, malloc doesn't initialize the allocated space, whereas calloc sets
it to all-bits-zero. But setting it to all-bits-zero doesn't
guarantee that all elements will be set to 0.0, since there's no
guarantee that all-bits-zero is a representation of all-bits-zero.

You can probably get away with it on most (maybe all?) existing
systems, but it's not portable. For guaranteed portability, you need
to set the elements to 0.0 using a loop -- and if you're going to do
that anyway you might as well just use malloc rather than calloc.
And, depending on what you're doing with the data, you might not need
to set *all* the elements to 0.0.

[...]

--
Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Keith Thompson, Jun 3, 2009
5. ### Flash GordonGuest

Keith Thompson wrote:
> Jean-Claude Arbaut <> writes:
>> Barry Schwarz wrote:
>>> On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
>>> <> wrote:

> [...]
>>>> t = (double (*)[])calloc(n*p, sizeof(double));
>>> Surely you meant u here, not t.

>> yes
>>
>>> The cast is unnecessary.

>> Someone just told me that on fr.clc ;-) Actually it's
>> a bad habit I got from using a bad compiler that gave
>> a warning for uncasted (mc)alloc. I can't remember
>> which compiler it was.

>
> Could it have been a C++ compiler?
>
>>> Furthermore,
>>> all bits zero need not be the representation of 0.0. A frequently
>>> recommended construct is
>>> t = calloc(n, sizeof *t);
>>> or the malloc "almost equivalent"
>>> t malloc (n * sizeof *t);

>> Does it guarantee that memory is zeroed ?

>
> No, malloc doesn't initialize the allocated space, whereas calloc sets
> it to all-bits-zero. But setting it to all-bits-zero doesn't
> guarantee that all elements will be set to 0.0, since there's no
> guarantee that all-bits-zero is a representation of all-bits-zero.

I know you meant "since there's no guarantee that all-bits-zero
represents a floating point 0.0 (or a null pointer)".

> You can probably get away with it on most (maybe all?) existing
> systems, but it's not portable. For guaranteed portability, you need
> to set the elements to 0.0 using a loop -- and if you're going to do
> that anyway you might as well just use malloc rather than calloc.
> And, depending on what you're doing with the data, you might not need
> to set *all* the elements to 0.0.

If the OP does choose to use calloc, make sure the non-portable
assumption is documented just in case someone ports the SW later to an
unusual system where it is a problem.
--
Flash Gordon

Flash Gordon, Jun 3, 2009
6. ### luserXtrogGuest

On Jun 3, 1:03 pm, Flash Gordon <> wrote:
> Keith Thompson wrote:
> > Jean-Claude Arbaut <> writes:
> >> Barry Schwarz wrote:
> >>> On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
> >>> <> wrote:

> > [...]
> >>>> t = (double (*)[])calloc(n*p, sizeof(double));
> >>> Surely you meant u here, not t.
> >> yes

>
> >>> The cast is unnecessary.
> >> Someone just told me that on fr.clc ;-) Actually it's
> >> a bad habit I got from using a bad compiler that gave
> >> a warning for uncasted (mc)alloc. I can't remember
> >> which compiler it was.

>
> > Could it have been a C++ compiler?

>
> >>> Furthermore,
> >>> all bits zero need not be the representation of 0.0.  A frequently
> >>> recommended construct is
> >>>    t = calloc(n, sizeof *t);
> >>> or the malloc "almost equivalent"
> >>>    t  malloc (n * sizeof *t);
> >> Does it guarantee that memory is zeroed ?

>
> > No, malloc doesn't initialize the allocated space, whereas calloc sets
> > it to all-bits-zero.  But setting it to all-bits-zero doesn't
> > guarantee that all elements will be set to 0.0, since there's no
> > guarantee that all-bits-zero is a representation of all-bits-zero.

>
> I know you meant "since there's no guarantee that all-bits-zero
> represents a floating point 0.0 (or a null pointer)".
>
> > You can probably get away with it on most (maybe all?) existing
> > systems, but it's not portable.  For guaranteed portability, you need
> > to set the elements to 0.0 using a loop -- and if you're going to do
> > that anyway you might as well just use malloc rather than calloc.
> > And, depending on what you're doing with the data, you might not need
> > to set *all* the elements to 0.0.

>
> If the OP does choose to use calloc, make sure the non-portable
> assumption is documented just in case someone ports the SW later to an
> unusual system where it is a problem.

Sounds like a good spot for an assertion;

assert(u[0][0]==0.0);

--
lxt

luserXtrog, Jun 3, 2009
7. ### James KuyperGuest

luserXtrog wrote:
> On Jun 3, 1:03 pm, Flash Gordon <> wrote:
>> Keith Thompson wrote:

....
>>> it to all-bits-zero. But setting it to all-bits-zero doesn't
>>> guarantee that all elements will be set to 0.0, since there's no
>>> guarantee that all-bits-zero is a representation of all-bits-zero.

>> I know you meant "since there's no guarantee that all-bits-zero
>> represents a floating point 0.0 (or a null pointer)".

....
> Sounds like a good spot for an assertion;
>
> assert(u[0][0]==0.0);

Perhaps - but keep in mind that if all-bits-zero is a trap
representation for 'double', the left operand of the comparison
expression has undefined behavior, which means (among other less
annoying possibilities), that your assert might fail to trigger. That
pretty much eliminates it's usefulness.

For similar purposes I've used a memcmp() between a zero-initialized
object and an object of the same size memset() to all-bits-0. That
completely avoids the undefined behavior mentioned above. Unfortunately,
it doesn't cover the possibility that all-bits-zero represents a value
equivalent to a zero-initialized variable, but with a different
representation. I was willing to live with that possibility; it would
only have made my program slightly inefficient - it wouldn't have broken it.

James Kuyper, Jun 3, 2009
8. ### Barry SchwarzGuest

On Wed, 03 Jun 2009 10:36:43 +0200, Jean-Claude Arbaut
<> wrote:

>Barry Schwarz wrote:
>
>> On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
>> <> wrote:
>>
>>> Hi,
>>>
>>> I can use:
>>>
>>> double get(int n, int p, double t[n][p], int i, int j) {
>>> return t[j];
>>> }
>>>
>>> and in another function
>>> double (*u)[];

>>
>> I believe this would need to be
>> double (*u)[p];
>> as in examples 3 and 4 in 6.7.5.2(9)-(10)

>
>There is one in n1256.pdf, p41
>And I'm not a C standard expert, but I tought
>it's legal to have one incomplete dimension.

The example on page 41 shows a pair of declarations, each of which is
partially incomplete. But as set, none of the incomplete parts remain
incomplete.

>
>gcc -Wall -std=c99 -pedantic doesn't bark at me.
>I know that doesn't prove much, but that's strange.
>
>
>
>>> t = (double (*)[])calloc(n*p, sizeof(double));

>>
>> Surely you meant u here, not t.

>
>yes
>
>> The cast is unnecessary.

>
>Someone just told me that on fr.clc ;-) Actually it's
>a bad habit I got from using a bad compiler that gave
>a warning for uncasted (mc)alloc. I can't remember
>which compiler it was.
>
>> Furthermore,
>> all bits zero need not be the representation of 0.0. A frequently
>> recommended construct is
>> t = calloc(n, sizeof *t);
>> or the malloc "almost equivalent"
>> t malloc (n * sizeof *t);

That should have been
t = malloc(n*sizeof *t)

>
>Does it guarantee that memory is zeroed ?
>
>>> a=get(n,p,u,i,j);
>>>
>>> Now, how would I declare u for 3 dimensions or more ?

>>
>> When declaring arrays in functions and pointers to arrays, only the
>> high-order dimension can be unspecified/omitted.

>
>Same remark as above.
>
>
>> If t3 is going to be defined in the function as
>> double t3[x][y][z]
>> then u3 will need to be defined as
>> double (*u3)[y][z];

>
>
>Is there really no way to allocate it ?
>I mean, it's possible with a warning to do
>
>double *p = malloc(100*sizeof(double));
>a = get(10,10,p,0,0);

This is not correct. Even if the compiler calls the diagnostic a
warning you still invoke undefined behavior. The function is
expecting a double(*)[p] and you are passing a double*. There is no
guarantee that the two pointers have the same representation or
alignment. Also there are several discussions in the archive if it is
legal to step through a 2D array as if it were a long 1D array.

>
>But what should I do to declare it correctly ?

I showed the definition of the array and the pointer above. You
assign allocated memory to the pointer with
u3 = calloc(x, sizeof *u3);
or the malloc "almost equivalent"
u3 = malloc (x * sizeof *u3);
the same as I showed for a 2D pointer.

--
Remove del for email

Barry Schwarz, Jun 4, 2009
9. ### Scarlet PimpernelGuest

Keith Thompson wrote:
> Jean-Claude Arbaut <> writes:

>> Someone just told me that on fr.clc ;-) Actually it's
>> a bad habit I got from using a bad compiler that gave
>> a warning for uncasted (mc)alloc. I can't remember
>> which compiler it was.

>
> Could it have been a C++ compiler?

Maybe. And I guess that would mean the warning is expected

>>> Furthermore,
>>> all bits zero need not be the representation of 0.0. A frequently
>>> recommended construct is
>>> t = calloc(n, sizeof *t);
>>> or the malloc "almost equivalent"
>>> t malloc (n * sizeof *t);

>> Does it guarantee that memory is zeroed ?

>
> No, malloc doesn't initialize the allocated space, whereas calloc sets
> it to all-bits-zero. But setting it to all-bits-zero doesn't
> guarantee that all elements will be set to 0.0, since there's no
> guarantee that all-bits-zero is a representation of all-bits-zero.
>
> You can probably get away with it on most (maybe all?) existing
> systems, but it's not portable. For guaranteed portability, you need
> to set the elements to 0.0 using a loop -- and if you're going to do
> that anyway you might as well just use malloc rather than calloc.
> And, depending on what you're doing with the data, you might not need
> to set *all* the elements to 0.0.

I think I would use calloc which would be both correct and
faster on almost all machine, and document the lack of
portability.

Scarlet Pimpernel, Jun 4, 2009
10. ### Scarlet PimpernelGuest

Barry Schwarz wrote:

> I showed the definition of the array and the pointer above. You
> assign allocated memory to the pointer with
> u3 = calloc(x, sizeof *u3);
> or the malloc "almost equivalent"
> u3 = malloc (x * sizeof *u3);
> the same as I showed for a 2D pointer.
>

Ok, actually, I had in mind a declaration of multidimensional
array without dimensions, but it would probably never be
necessary, since you must know the dimensions to allocate
it.

Another possible problem, you can declare a double[p][q][r] and pass
it to a function that will use double[q][r][p]. I suppose
it's still valid C ?

And, another idea, would it be correct to use a void* like here:

double get(int n, int p, int q, double t[n][p][q],int i, int j, int k) {
return t[j][k];
}

double toto() {
int n,p,q;
double x;
void *a;
n = 100;
p = 200;
q = 300;
a = malloc(n*p*q*sizeof(double));
x = get(n,p,q,a,0,0,0);
return x;
}

(gcc gives no warning)

Scarlet Pimpernel, Jun 4, 2009
11. ### Scarlet PimpernelGuest

Scarlet Pimpernel wrote:
> Barry Schwarz wrote:
>
>> I showed the definition of the array and the pointer above. You
>> assign allocated memory to the pointer with
>> u3 = calloc(x, sizeof *u3);
>> or the malloc "almost equivalent"
>> u3 = malloc (x * sizeof *u3);
>> the same as I showed for a 2D pointer.
>>

>
> Ok, actually, I had in mind a declaration of multidimensional
> array without dimensions, but it would probably never be
> necessary, since you must know the dimensions to allocate
> it.
>
> Another possible problem, you can declare a double[p][q][r] and pass
> it to a function that will use double[q][r][p]. I suppose
> it's still valid C ?
>
> And, another idea, would it be correct to use a void* like here:
>
> double get(int n, int p, int q, double t[n][p][q],int i, int j, int k) {
> return t[j][k];
> }
>
> double toto() {
> int n,p,q;
> double x;
> void *a;
> n = 100;
> p = 200;
> q = 300;
> a = malloc(n*p*q*sizeof(double));
> x = get(n,p,q,a,0,0,0);
> return x;
> }
>
> (gcc gives no warning)

The change of pseudo in a thread may be annoying, sorry for this !

Scarlet Pimpernel, Jun 4, 2009
12. ### Barry SchwarzGuest

On Thu, 04 Jun 2009 23:59:38 +0200, Scarlet Pimpernel
<> wrote:

>Barry Schwarz wrote:
>
>> I showed the definition of the array and the pointer above. You
>> assign allocated memory to the pointer with
>> u3 = calloc(x, sizeof *u3);
>> or the malloc "almost equivalent"
>> u3 = malloc (x * sizeof *u3);
>> the same as I showed for a 2D pointer.
>>

>
>Ok, actually, I had in mind a declaration of multidimensional
>array without dimensions, but it would probably never be
>necessary, since you must know the dimensions to allocate
>it.
>
>Another possible problem, you can declare a double[p][q][r] and pass
>it to a function that will use double[q][r][p]. I suppose
>it's still valid C ?

No. It is never OK to lie to the compiler.

In this case, a diagnostic is required and the compiler is within its
rights to reject the program.

>
>And, another idea, would it be correct to use a void* like here:
>
>double get(int n, int p, int q, double t[n][p][q],int i, int j, int k) {
> return t[j][k];
>}
>
>double toto() {
> int n,p,q;
> double x;
> void *a;
> n = 100;
> p = 200;
> q = 300;
> a = malloc(n*p*q*sizeof(double));
> x = get(n,p,q,a,0,0,0);
> return x;
>}

Syntactically it is acceptable because the fourth parameter is
actually a pointer (even though it looks like an array) and a void
pointer may be used as the argument to assign a value to the
parameter. The problem of stepping through a multi-dimensional array
as if it were a very long 1D array remains.

>(gcc gives no warning)

--
Remove del for email

Barry Schwarz, Jun 5, 2009
13. ### JosephKKGuest

On Wed, 03 Jun 2009 19:03:33 +0100, Flash Gordon
<> wrote:

>Keith Thompson wrote:
>> Jean-Claude Arbaut <> writes:
>>> Barry Schwarz wrote:
>>>> On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
>>>> <> wrote:

>> [...]
>>>>> t = (double (*)[])calloc(n*p, sizeof(double));
>>>> Surely you meant u here, not t.
>>> yes
>>>
>>>> The cast is unnecessary.
>>> Someone just told me that on fr.clc ;-) Actually it's
>>> a bad habit I got from using a bad compiler that gave
>>> a warning for uncasted (mc)alloc. I can't remember
>>> which compiler it was.

>>
>> Could it have been a C++ compiler?
>>
>>>> Furthermore,
>>>> all bits zero need not be the representation of 0.0. A frequently
>>>> recommended construct is
>>>> t = calloc(n, sizeof *t);
>>>> or the malloc "almost equivalent"
>>>> t malloc (n * sizeof *t);
>>> Does it guarantee that memory is zeroed ?

>>
>> No, malloc doesn't initialize the allocated space, whereas calloc sets
>> it to all-bits-zero. But setting it to all-bits-zero doesn't
>> guarantee that all elements will be set to 0.0, since there's no
>> guarantee that all-bits-zero is a representation of all-bits-zero.

>
>I know you meant "since there's no guarantee that all-bits-zero
>represents a floating point 0.0 (or a null pointer)".

Absolutely true. However, all zeroes is defined by IEEE-754 to be a
valid representation of floating point 0.0, and the following machines
are compliant: the entire X86 series, the M68K series (including
coldfire), PPC, the NS32K series, UltraSparc and above, Alpha chips,
at least some MIPS chips, HP PA-RISC, and probably others. Known non-
compliant implementations include IBM 360/370/390 series, VAXen,
probably DEC PDP-8, PDP-10, and PDP-11, CDC Cyber series, and
certainly many more.

Moreover, even in the non IEEE-754 compliant systems there is a
hardware advantage to making all zeroes be a FP 0.0, and such is the
case for VAX and probably others.

Bottom line, not required to be portable, chances it works anyway
very, very high.
>
>> You can probably get away with it on most (maybe all?) existing
>> systems, but it's not portable. For guaranteed portability, you need
>> to set the elements to 0.0 using a loop -- and if you're going to do
>> that anyway you might as well just use malloc rather than calloc.
>> And, depending on what you're doing with the data, you might not need
>> to set *all* the elements to 0.0.

>
>If the OP does choose to use calloc, make sure the non-portable
>assumption is documented just in case someone ports the SW later to an
>unusual system where it is a problem.

JosephKK, Jun 6, 2009
14. ### Barry SchwarzGuest

On Fri, 05 Jun 2009 19:38:05 -0700,
"JosephKK"<> wrote:

snip

>Absolutely true. However, all zeroes is defined by IEEE-754 to be a
>valid representation of floating point 0.0, and the following machines
>are compliant: the entire X86 series, the M68K series (including
>coldfire), PPC, the NS32K series, UltraSparc and above, Alpha chips,
>at least some MIPS chips, HP PA-RISC, and probably others. Known non-
>compliant implementations include IBM 360/370/390 series, VAXen,

While it is true that early IBM series do not comply with IEEE-754
(hardly noteworthy since the 360 preceded 754 by a few decades), all
bits zero is a representation of 0.0 and the only normalized
representation.

--
Remove del for email

Barry Schwarz, Jun 6, 2009
15. ### Tim RentschGuest

"JosephKK"<> writes:

> On Wed, 03 Jun 2009 19:03:33 +0100, Flash Gordon
>[snip]
> Absolutely true. However, all zeroes is defined by IEEE-754 to be a
> valid representation of floating point 0.0, and the following machines
> are compliant: the entire X86 series, the M68K series (including
> coldfire), PPC, the NS32K series, UltraSparc and above, Alpha chips,
> at least some MIPS chips, HP PA-RISC, and probably others. Known non-
> compliant implementations include IBM 360/370/390 series, VAXen,
> probably DEC PDP-8, PDP-10, and PDP-11, CDC Cyber series, and
> certainly many more.
>
> Moreover, even in the non IEEE-754 compliant systems there is a
> hardware advantage to making all zeroes be a FP 0.0, and such is the
> case for VAX and probably others.

On the PDP-10 all-bits-zero is a 0.0 FP value. Did the PDP-8
even have floating point? I didn't think it did.

Tim Rentsch, Jun 6, 2009
16. ### David ThompsonGuest

On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
<> wrote:

> Hi,
>
> I can use:
>
> double get(int n, int p, double t[n][p], int i, int j) {
> return t[j];
> }
>

Note that using a variable (here a parameter) as an array bound is
standard only as of C99, although it was available as an extension in
gcc (which you say elsethread you use) years earlier.

> and in another function
> double (*u)[];
> t = (double (*)[])calloc(n*p, sizeof(double));
> a=get(n,p,u,i,j);
>
> Now, how would I declare u for 3 dimensions or more ?

With the C99 or gcc Variable Length Array feature, you can _define_
(allocate) a runtime size array just using the natural
double u [n1] [n2] [n3] [n4]; /* or whatever */
assuming those variables are validly set, and space is available.

If you do want dynamic, you can declare the non-first bounds correctly
double (*u) [n2] [n3] [n4];

OR you can use any arbitrary (fixed) value(s)
double (*u) [1] [1] [1];
AS LONG AS you don't use _that_ pointer to access _components_
(including subarrays) but only pass it to things like your get ()
which use a (var-mod) pointer _with correct bounds_ for accesses.
(Note that t in get() is actually a pointer, not an array.)

Specifically, (pointer to) variable-size of T is compatible with
(pointer to) ANY fixed-size of T, so only the 'structure' (i.e. rank
and element type) must match not the specific bound values.

Note that in most implementations automatic objects are allocated on
the (single, preferred) stack, and dynamic space in a heap 'arena',
and rather often the latter is or at least can be larger than the
former. This applies to both fixed types (arrays with compile-time
bounds) and variable ones, but IME people more often want to use
variable ones to handle large (or huge) data sizes and thus more often
run into an actual problem there.

David Thompson, Jun 15, 2009
17. ### Tim RentschGuest

David Thompson <> writes:

> On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
> <> wrote:
>
> > Hi,
> >
> > I can use:
> >
> > double get(int n, int p, double t[n][p], int i, int j) {
> > return t[j];
> > }
> >

> Note that using a variable (here a parameter) as an array bound is
> standard only as of C99, although it was available as an extension in
> gcc (which you say elsethread you use) years earlier.
>
> > and in another function
> > double (*u)[];
> > t = (double (*)[])calloc(n*p, sizeof(double));
> > a=get(n,p,u,i,j);
> >
> > Now, how would I declare u for 3 dimensions or more ?

>
> With the C99 or gcc Variable Length Array feature, you can _define_
> (allocate) a runtime size array just using the natural
> double u [n1] [n2] [n3] [n4]; /* or whatever */
> assuming those variables are validly set, and space is available.
>
> If you do want dynamic, you can declare the non-first bounds correctly
> double (*u) [n2] [n3] [n4];

Or all the bounds:

double (*u)[n1][n2][n3][n4];

and use (*u) to access the entire array, or as a parameter
for functions taking a (double [n1][n2][n3][n4]).

> OR you can use any arbitrary (fixed) value(s)
> double (*u) [1] [1] [1];
> AS LONG AS you don't use _that_ pointer to access _components_
> (including subarrays) but only pass it to things like your get ()
> which use a (var-mod) pointer _with correct bounds_ for accesses.
> (Note that t in get() is actually a pointer, not an array.)

This suggestion seems dubious. The types are compatible, yes, but.....

> Specifically, (pointer to) variable-size of T is compatible with
> (pointer to) ANY fixed-size of T, so only the 'structure' (i.e. rank
> and element type) must match not the specific bound values.

Passing an array declared with fixed bounds to a routine that
expects a pointer-to-variable-length-array is a no-no (assuming
the bounds don't match). The second sentence of 6.7.5.2 p 6
says:

If the two array types are used in a context which requires
them to be compatible, it is undefined behavior if the two
size specifiers evaluate to unequal values.

Considering this condition, it's better to use one of the
pointer-to-variable-length-array types as shown above, or
just use a variable of type (double *) and cast it.

Tim Rentsch, Jun 19, 2009