variable length array

  • Thread starter Jean-Claude Arbaut
  • Start date
J

Jean-Claude Arbaut

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 ?
 
B

Barry Schwarz

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];
 
J

Jean-Claude Arbaut

Barry said:
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 ?
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 ?
 
K

Keith Thompson

Jean-Claude Arbaut said:
Barry said:
On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
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?
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.

[...]
 
F

Flash Gordon

Keith said:
Jean-Claude Arbaut said:
Barry said:
On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
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?
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.
 
L

luserXtrog

Keith said:
Jean-Claude Arbaut said:
Barry Schwarz wrote:
On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
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?
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);
 
J

James Kuyper

luserXtrog said:
Keith Thompson wrote: ....
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.
 
B

Barry Schwarz

Barry said:
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 ?
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.
 
S

Scarlet Pimpernel

Keith said:
Could it have been a C++ compiler?

Maybe. And I guess that would mean the warning is expected :)
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.
 
S

Scarlet Pimpernel

Barry said:
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)
 
S

Scarlet Pimpernel

Scarlet said:
Barry said:
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 !
 
B

Barry Schwarz

Barry said:
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.
 
J

JosephKK

Keith said:
Jean-Claude Arbaut said:
Barry Schwarz wrote:
On Wed, 03 Jun 2009 02:16:55 +0200, Jean-Claude Arbaut
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.
 
B

Barry Schwarz

On Fri, 05 Jun 2009 19:38:05 -0700,

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.
 
T

Tim Rentsch

JosephKK said:
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.
 
D

David Thompson

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.
 
T

Tim Rentsch

David Thompson said:
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.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top