multidimensional heap arrays

J

Jack Boot

hi

we have been discussing methods for multidimensional heap arrays, between
us we have 3 preferred approaches

1: array of pointers each mallocated, ie
int ** A = malloc(nrow * sizeof(int*));
for(i=0;i<nrow;i++) A=malloc(ncol * sizeof(int));

2: array of pointers set into one big mallocated array, ie
int ** A = malloc(nrow * sizeof(int*));
int * Astorage = malloc(nrow * ncol * sizeof(int));
for(i=0;i<nrow;i++) A=Astorage+i*ncol;

3: one big mallocated array, no separate row pointers but an accessor
macro instead, ie
int * Astorage = malloc(nrow * ncol * sizeof(int));
#define A(i,j) Astorage[i*ncol+j];

what is your opinion of these methods? what situations is one is better
than another? should some of them never be used? what is standard/best
practice?

cheers
JB
 
F

Francois Grieu

hi

we have been discussing methods for multidimensional heap arrays, between
us we have 3 preferred approaches

1: array of pointers each mallocated, ie
int ** A = malloc(nrow * sizeof(int*));
for(i=0;i<nrow;i++) A=malloc(ncol * sizeof(int));


Except for the lack of error checking, this one seems OK to me.
Contrary to the other two, it has a chance to work when the heap
is fragmented, or there otherwise exists a restriction on the size
of allocated object.
2: array of pointers set into one big mallocated array, ie
int ** A = malloc(nrow * sizeof(int*));
int * Astorage = malloc(nrow * ncol * sizeof(int));
for(i=0;i<nrow;i++) A=Astorage+i*ncol;


This one could be subject to overflow when computing nrow * ncol;
it can be improved: Astorage = malloc(ncol * sizeof(int) * nrow).

3: one big mallocated array, no separate row pointers but an accessor
macro instead, ie
int * Astorage = malloc(nrow * ncol * sizeof(int));
#define A(i,j) Astorage[i*ncol+j];

That macro should be
#define A(i,j) Astorage[(i)*(size_t(ncol)+(j)]
in order to work e.g. when i is an expression, and to avoid the
the same overflow problem. And then there is the issue of the scope
of Astorage and ncol, and one should #undef A when either becomes
out of scope.
What is your opinion of these methods? What situations is one is better
than another? Should some of them never be used? What is standard/best
practice?

The first is the most likely to work. It is arguably the most standard
and closest to best practice.
The third may be noticeably faster - or not.
None is best practice, for lack of proper error check.


Francois Grieu
 
B

Ben Bacarisse

Jack Boot said:
we have been discussing methods for multidimensional heap arrays, between
us we have 3 preferred approaches

1: array of pointers each mallocated, ie
int ** A = malloc(nrow * sizeof(int*));
for(i=0;i<nrow;i++) A=malloc(ncol * sizeof(int));


A useful idiom when assigning allocated storage to E = malloc(...) is to
use sizeof *E as the size. By keeping the pattern the same, you can
avoid accidentally using the wrong size. So I'd write:

int **A = malloc(nrow * sizeof *A);
if (A)
for (i = 0; i < nrow; i++)
A = malloc(ncol * sizeof *A);
2: array of pointers set into one big mallocated array, ie
int ** A = malloc(nrow * sizeof(int*));
int * Astorage = malloc(nrow * ncol * sizeof(int));
for(i=0;i<nrow;i++) A=Astorage+i*ncol;

3: one big mallocated array, no separate row pointers but an accessor
macro instead, ie
int * Astorage = malloc(nrow * ncol * sizeof(int));
#define A(i,j) Astorage[i*ncol+j];


You can avoid the macro by allocating (and thus pointing to) an array
type:

int (*A)[ncol] = malloc(nrow * sizeof *A);

The sizeof idiom really pays off here: there is no need to think about
what the right type element type is.

If ncol is not a constant expression, you will need a modern C compiler
for this to work (by which I mean C99). Use them while you can, since
they will no longer be required in the next C standard.
what is your opinion of these methods? what situations is one is better
than another? should some of them never be used? what is standard/best
practice?

Sorry, no idea. It is sometimes possible to pick the "best" if enough is
known about the problem being solved and the constraints on the solution
but I don't think there is a "best" without any such information.
 
E

Eric Sosman

hi

we have been discussing methods for multidimensional heap arrays, between
us we have 3 preferred approaches

1: array of pointers each mallocated, ie
int ** A = malloc(nrow * sizeof(int*));
for(i=0;i<nrow;i++) A=malloc(ncol * sizeof(int));

2: array of pointers set into one big mallocated array, ie
int ** A = malloc(nrow * sizeof(int*));
int * Astorage = malloc(nrow * ncol * sizeof(int));
for(i=0;i<nrow;i++) A=Astorage+i*ncol;

3: one big mallocated array, no separate row pointers but an accessor
macro instead, ie
int * Astorage = malloc(nrow * ncol * sizeof(int));
#define A(i,j) Astorage[i*ncol+j];

what is your opinion of these methods? what situations is one is better
than another? should some of them never be used? what is standard/best
practice?


All need some cleanup, of course: Error-checking on the malloc()
calls, extra parentheses in the macro, better sizeof operands, etc.

(1) uses the most storage, (3) the least. (2) is somewhere in
between: It uses the same nominal amount as (1), but by using fewer
allocations it uses less per-allocation overhead.

(1) is least likely to run afoul of per-allocation size limits
(including limits caused by fragmentation).

(1) is the most vulnerable to performance penalties, because of
the double indirection and the possible lack of locality among the
individual sub-allocations. (2) suffers the indirection but not the
locality problems, and (3) suffers neither. But this must be taken
ith a grain of salt, or maybe even a truckload, because access patterns
and cache characteristics and can interact in hard-to-foresee ways.

Which would I choose? Well, what day of the week is it, and
what's the phase of the Moon, and was my coffee bitter this morning?
More to the point, what fits most neatly into the rest of the program?
If the individual "rows" have a meaning of their own, I'd probably opt
for (1) or (2), but if it's just an "atomic" matrix I might use (3).
Or not. Or, as a wise man once said: "When you cannot choose between
A and B, flip a coin. As the coin starts descending from the height
of its arc, you'll realize which way you want it to fall."
 
J

James Kuyper

hi

we have been discussing methods for multidimensional heap arrays, between
us we have 3 preferred approaches

1: array of pointers each mallocated, ie
int ** A = malloc(nrow * sizeof(int*));
for(i=0;i<nrow;i++) A=malloc(ncol * sizeof(int));


I've used this approach before. It's particularly useful when the rows
of the array have different lengths, and in a triangular matrix. With
careful attention to how you access such a matrix, you can avoid storing
all of the useless zero elements.
2: array of pointers set into one big mallocated array, ie
int ** A = malloc(nrow * sizeof(int*));
int * Astorage = malloc(nrow * ncol * sizeof(int));
for(i=0;i<nrow;i++) A=Astorage+i*ncol;


I've used something like this approach before, but I don't like it. I
used it because I was trying to reverse-engineer some routines from a
commercial third-party library (I told my boss I wanted to get advice
from a corporate lawyer before I tackled this project - he told me to
just get on with it, and not worry about such issues. I filled the code
with comments documenting which library my code was emulating, as well
as exactly how I determine what the functions should do without
disassembling it or stealing the original source code. I hope that was
sufficient to avoid legal problems.)

One of these routines created a dynamically-allocated structure filled
with pointers to arrays of several different dimensions, whose length in
each dimension was variable, and stored in that structure. My black-box
testing determined that it was using something like the following
method: it allocated a single block of memory big enough to store the
structure and all of the arrays it contained pointers to. The arrays
were not merely of several different sizes, but even of several
different dimensions. The routine filled in the pointers in that
structure with values pointing at various parts of that single big
allocation.

I perforce followed the same approach in my own reverse-engineered code.
I can see the advantages of such an approach, but it strikes me as a
delicate system, easily broken if a single pointer gets set incorrectly
during an update.
3: one big mallocated array, no separate row pointers but an accessor
macro instead, ie
int * Astorage = malloc(nrow * ncol * sizeof(int));
#define A(i,j) Astorage[i*ncol+j];

what is your opinion of these methods? what situations is one is better
than another? should some of them never be used? what is standard/best
practice?

I'd replace the accessor macro with an inline function, if only for type
safety. Otherwise, this is another reasonable approach.
 
A

Anand Hariharan

3: one big mallocated array, no separate row pointers but an accessor
macro instead, ie
int * Astorage = malloc(nrow * ncol * sizeof(int));
#define A(i,j) Astorage[i*ncol+j];

You can avoid the macro by allocating (and thus pointing to) an array
type:

  int (*A)[ncol] = malloc(nrow * sizeof *A);

The sizeof idiom really pays off here: there is no need to think about
what the right type element type is.

If ncol is not a constant expression, you will need a modern C compiler
for this to work (by which I mean C99).  Use them while you can, since
they will no longer be required in the next C standard.

Could you please elaborate? Is the next standard getting rid of VLAs
or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

.... or did you mean something else entirely?

- Anand
 
F

Francois Grieu

3: one big mallocated array, no separate row pointers but an accessor
macro instead, ie
int * Astorage = malloc(nrow * ncol * sizeof(int));
#define A(i,j) Astorage[i*ncol+j];

You can avoid the macro by allocating (and thus pointing to) an array
type:

int (*A)[ncol] = malloc(nrow * sizeof *A);

The sizeof idiom really pays off here: there is no need to think about
what the right type element type is.

If ncol is not a constant expression, you will need a modern C compiler
for this to work (by which I mean C99). Use them while you can, since
they will no longer be required in the next C standard.

Could you please elaborate? Is the next standard getting rid of VLAs (..)

The next standard is making VLAs optional. Under ISO/IEC 9899:1999 VLAs
are mandated (and some compilers have not implemented it).
or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

I'm genuinely wondering: is ncol a "constant expression" ?

Francois Grieu
 
B

Ben Bacarisse

Francois Grieu said:
3: one big mallocated array, no separate row pointers but an accessor
macro instead, ie
int * Astorage = malloc(nrow * ncol * sizeof(int));
#define A(i,j) Astorage[i*ncol+j];

You can avoid the macro by allocating (and thus pointing to) an array
type:

int (*A)[ncol] = malloc(nrow * sizeof *A);

The sizeof idiom really pays off here: there is no need to think about
what the right type element type is.

If ncol is not a constant expression, you will need a modern C compiler
for this to work (by which I mean C99). Use them while you can, since
they will no longer be required in the next C standard.

Could you please elaborate? Is the next standard getting rid of VLAs (..)

The next standard is making VLAs optional. Under ISO/IEC 9899:1999 VLAs
are mandated (and some compilers have not implemented it).

I suppose it is worth saying that this is the current plan though I
don't think there is any chance it will be changed. It's very unlikely
that any compiler that currently implements VLAs will stop doing so, but
it does alter their status as a language feature. I'd hoped that the
very useful concurrency in the new C would have acted as a spur to
implementers this finally getting C99 widely adopted but, presumably,
the committee felt that parts of C99 must be optional to have any change
of the new standard getting adopted.
or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

The former. This is a VLA and will remain so in C1x.
I'm genuinely wondering: is ncol a "constant expression" ?

It might be in that an implementation can define its own forms of
constant expression, but it does not meet the standard's minimum
requirements to be an "integer constant expression" (which is what is
required to declare a non-VL array).
 
I

Ian Collins

Francois Grieu said:
On 26/04/2011 06:19, Anand Hariharan wrote:
or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

The former. This is a VLA and will remain so in C1x.
I'm genuinely wondering: is ncol a "constant expression" ?

It might be in that an implementation can define its own forms of
constant expression, but it does not meet the standard's minimum
requirements to be an "integer constant expression" (which is what is
required to declare a non-VL array).

Which is really silly. I do wish they could fix that and bring it in
line with C++. But I guess adding VLAs shot that idea in the foot.
 
B

Ben Bacarisse

Ian Collins said:
Francois Grieu said:
On 26/04/2011 06:19, Anand Hariharan wrote:
or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

The former. This is a VLA and will remain so in C1x.
I'm genuinely wondering: is ncol a "constant expression" ?

It might be in that an implementation can define its own forms of
constant expression, but it does not meet the standard's minimum
requirements to be an "integer constant expression" (which is what is
required to declare a non-VL array).

Which is really silly. I do wish they could fix that and bring it in
line with C++. But I guess adding VLAs shot that idea in the foot.

I too would like to have seen the same constant expression definition
between C++ and C but I don't see how C's VLAs got in the way of that.
 
I

Ian Collins

Ian Collins said:
On 26/04/2011 06:19, Anand Hariharan wrote:

or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

The former. This is a VLA and will remain so in C1x.

I'm genuinely wondering: is ncol a "constant expression" ?

It might be in that an implementation can define its own forms of
constant expression, but it does not meet the standard's minimum
requirements to be an "integer constant expression" (which is what is
required to declare a non-VL array).

Which is really silly. I do wish they could fix that and bring it in
line with C++. But I guess adding VLAs shot that idea in the foot.

I too would like to have seen the same constant expression definition
between C++ and C but I don't see how C's VLAs got in the way of that.

The code above sums it up, if ncol is an "integer constant expression"
is arr an array, or a VLA?

Would it be acceptable for a language change to change (all be it
subtly) the behaviour of existing code?
 
K

Keith Thompson

Ian Collins said:
Ian Collins said:
On 04/26/11 11:19 PM, Ben Bacarisse wrote:

On 26/04/2011 06:19, Anand Hariharan wrote:

or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

The former. This is a VLA and will remain so in C1x.

I'm genuinely wondering: is ncol a "constant expression" ?

It might be in that an implementation can define its own forms of
constant expression, but it does not meet the standard's minimum
requirements to be an "integer constant expression" (which is what is
required to declare a non-VL array).

Which is really silly. I do wish they could fix that and bring it in
line with C++. But I guess adding VLAs shot that idea in the foot.

I too would like to have seen the same constant expression definition
between C++ and C but I don't see how C's VLAs got in the way of that.

The code above sums it up, if ncol is an "integer constant expression"
is arr an array, or a VLA?

Would it be acceptable for a language change to change (all be it
subtly) the behaviour of existing code?

What behavior would change?

Currently, arr is a VLA. If the rules were changed to make ncol a
constant expression, as it is in C++, arr would be an ordinary array
object, not a VLA. As far as I can tell, this would merely make some
things legal and well-defined that are currently either constraint
violations or undefined behavior. I can't think of any cases were valid
code would either become invalid or change its (currently defined)
behavior. Can you?
 
B

Ben Bacarisse

Ian Collins said:
Ian Collins said:
On 04/26/11 11:19 PM, Ben Bacarisse wrote:

On 26/04/2011 06:19, Anand Hariharan wrote:

or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

The former. This is a VLA and will remain so in C1x.

I'm genuinely wondering: is ncol a "constant expression" ?

It might be in that an implementation can define its own forms of
constant expression, but it does not meet the standard's minimum
requirements to be an "integer constant expression" (which is what is
required to declare a non-VL array).

Which is really silly. I do wish they could fix that and bring it in
line with C++. But I guess adding VLAs shot that idea in the foot.

I too would like to have seen the same constant expression definition
between C++ and C but I don't see how C's VLAs got in the way of that.

The code above sums it up, if ncol is an "integer constant expression"
is arr an array, or a VLA?

Yes, a change in what constitutes an integer const expression would
change some VLAs to plain arrays but I don't think that counts as
"[shooting] that idea in the foot".
Would it be acceptable for a language change to change (all be it
subtly) the behaviour of existing code?

I think so, in part because I am not certain that one can rely on the
current behaviour. An implementation that permitted initialised const
variables to appear in constant expressions would already have the "new"
semantics (at least I think it would) so it could be argued that the
code was skating on thin ice already.

In addition, the change is very sight. A program that behaves
differently were a VLA to become a plain arrays is already something of
a monster. I can come up with such programs, but they are all very
contrived. There may be a natural example, but it seems unlikely.
 
I

Ian Collins

Ian Collins said:
On 04/26/11 11:19 PM, Ben Bacarisse wrote:

On 26/04/2011 06:19, Anand Hariharan wrote:

or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

The former. This is a VLA and will remain so in C1x.

I'm genuinely wondering: is ncol a "constant expression" ?

It might be in that an implementation can define its own forms of
constant expression, but it does not meet the standard's minimum
requirements to be an "integer constant expression" (which is what is
required to declare a non-VL array).

Which is really silly. I do wish they could fix that and bring it in
line with C++. But I guess adding VLAs shot that idea in the foot.

I too would like to have seen the same constant expression definition
between C++ and C but I don't see how C's VLAs got in the way of that.

The code above sums it up, if ncol is an "integer constant expression"
is arr an array, or a VLA?

Would it be acceptable for a language change to change (all be it
subtly) the behaviour of existing code?

What behavior would change?

Currently, arr is a VLA. If the rules were changed to make ncol a
constant expression, as it is in C++, arr would be an ordinary array
object, not a VLA. As far as I can tell, this would merely make some
things legal and well-defined that are currently either constraint
violations or undefined behavior. I can't think of any cases were valid
code would either become invalid or change its (currently defined)
behavior. Can you?

No I can't off hand, but I'm sure someone could (Ben?).

To be honest, I was struggling to find a reason why this obvious change
hadn't been made and VLAs were the only (if feeble) one I could find...
 
I

Ian Collins

Ian Collins said:
On 04/26/11 11:19 PM, Ben Bacarisse wrote:

On 26/04/2011 06:19, Anand Hariharan wrote:

or will the 'arr' in the following code be a 'plain and ordinary'
array ...

const int ncol = 5;
int arr[ncol];

The former. This is a VLA and will remain so in C1x.

I'm genuinely wondering: is ncol a "constant expression" ?

It might be in that an implementation can define its own forms of
constant expression, but it does not meet the standard's minimum
requirements to be an "integer constant expression" (which is what is
required to declare a non-VL array).

Which is really silly. I do wish they could fix that and bring it in
line with C++. But I guess adding VLAs shot that idea in the foot.

I too would like to have seen the same constant expression definition
between C++ and C but I don't see how C's VLAs got in the way of that.

The code above sums it up, if ncol is an "integer constant expression"
is arr an array, or a VLA?

Yes, a change in what constitutes an integer const expression would
change some VLAs to plain arrays but I don't think that counts as
"[shooting] that idea in the foot".
Would it be acceptable for a language change to change (all be it
subtly) the behaviour of existing code?

I think so, in part because I am not certain that one can rely on the
current behaviour. An implementation that permitted initialised const
variables to appear in constant expressions would already have the "new"
semantics (at least I think it would) so it could be argued that the
code was skating on thin ice already.

In addition, the change is very sight. A program that behaves
differently were a VLA to become a plain arrays is already something of
a monster. I can come up with such programs, but they are all very
contrived. There may be a natural example, but it seems unlikely.

I was trying to come up with one and the first example I conjured up
surprised me:

typedef char C[16];

void f( C* c ) {}

int main(void)
{
char c[16];

f( &c );

const unsigned n = 15;

char cc[n];

f( &cc );
}

Compiles fine as c99 (which was the surprise), but fails as C++ (type
mismatch). What did surprise me was the VLA type matching even though
the size differed. Changing the declaration of cc to cc[15] did produce
the expected compiler warnings.
 
B

Ben Bacarisse

Keith Thompson said:
Ian Collins <[email protected]> writes:

What behavior would change?

Currently, arr is a VLA. If the rules were changed to make ncol a
constant expression, as it is in C++, arr would be an ordinary array
object, not a VLA. As far as I can tell, this would merely make some
things legal and well-defined that are currently either constraint
violations or undefined behavior. I can't think of any cases were valid
code would either become invalid or change its (currently defined)
behavior. Can you?

I think I can, but only in the most obscure way. For example:

const int n = 10;
int vla[n];
int (*vlap)[n] = &vla;
int i = 0;
sizeof vlap[i++];

must make i == 1 if vla is a VLA and must leave it at zero otherwise.

For simplicity I've snipped the discussion that got us here so I feel I
should re-state that I don't think this matters much (to put it mildly).
 
K

Keith Thompson

Ben Bacarisse said:
Keith Thompson said:
Ian Collins <[email protected]> writes:

What behavior would change?

Currently, arr is a VLA. If the rules were changed to make ncol a
constant expression, as it is in C++, arr would be an ordinary array
object, not a VLA. As far as I can tell, this would merely make some
things legal and well-defined that are currently either constraint
violations or undefined behavior. I can't think of any cases were valid
code would either become invalid or change its (currently defined)
behavior. Can you?

I think I can, but only in the most obscure way. For example:

const int n = 10;
int vla[n];
int (*vlap)[n] = &vla;
int i = 0;
sizeof vlap[i++];

must make i == 1 if vla is a VLA and must leave it at zero otherwise.

For simplicity I've snipped the discussion that got us here so I feel I
should re-state that I don't think this matters much (to put it mildly).

Interesting case. I think it illustrates a problem with C99's definition
of sizeof. C99 6.5.3.4p2 says:

If the type of the operand is a variable length array type, the
operand is evaluated; otherwise, the operand is not evaluated
and the result is an integer constant.

I think this both causes things to be evaluated that needn't be, and
doesn't cause things to be evaluated that should be, though it probably
matters only in obscure cases.

In the simplest case of applying sizeof to a VLA:

int r = rand() % 10 + 1;
int vla[r];
sizeof vla;

I'm not sure what it even means here to evaluate ``vla''. Certainly it
has to evaluate *something*, but that something is probably an anonymous
variable. (In particular, it's not r, since r can be modified without
affecting the VLA.)

In this contrived example:

int i;
void *p;
size_t s = sizeof (i = 42, (int(*))p);

the argument to sizeof is not evaluated, even though the evaluation
of ``i = 42'' is needed for the type ``int(*)'' to be valid.
On the other hand, in this case what's really being evaluated is
the size of a pointer, which probably (certainly?) doesn't actually
depend on the value of i.

Of course sizeof can also take a parenthesized type name as its
argument:

sizeof(int[rand() % 10 + 1]);

This is "evaluating" a type name. It's not at all clear what
that means, but presumably it includes evaluating the expression
``rand() % 10 + 1''.

Would this work?

If the operand is an expression, it is not evaluated. If the
operand is a parenthesized type name, each full expression that
is part of the type name is evaluated.

A VLA is the only case I can think of where a type name can include a
non-constant expression.

Hmm. In ``sizeof (int[42])'' is the expression ``42'' "evaluated"? C99
6.6.2 implies that it is ("A constant expression can be evaluated during
translation rather than runtime"), but 6.5.3.4 kinda sorta implies that it
isn't. Does the description of sizeof need to distinguish between
translation-time and execution-time evaluation?
 
B

Ben Bacarisse

Ian Collins said:
On 04/27/11 09:49 AM, Ben Bacarisse wrote:
In addition, the change is very sight. A program that behaves
differently were a VLA to become a plain arrays is already something of
a monster. I can come up with such programs, but they are all very
contrived. There may be a natural example, but it seems unlikely.

I was trying to come up with one and the first example I conjured up
surprised me:

typedef char C[16];

void f( C* c ) {}

int main(void)
{
char c[16];

f( &c );

const unsigned n = 15;

char cc[n];

f( &cc );
}

Compiles fine as c99 (which was the surprise),

Hmm... At first glance it looks like a diagnostic is required. 6.7.5.2
p6 says:

"For two array types to be compatible, both shall have compatible
element types, and if both size specifiers are present, and are
integer constant expressions, then both size specifiers shall have the
same constant value. 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."

so are '&cc' and the parameter 'c' pointers to compatible types? This
paragraph does not say. Maybe it is specified somewhere else, but I
can't find it.

I think the intent of the last sentence is to make the program's
behaviour is undefined, but that would imply the need for a diagnostic.
but fails as C++ (type
mismatch). What did surprise me was the VLA type matching even though
the size differed. Changing the declaration of cc to cc[15] did
produce the expected compiler warnings.

The standard can't mandate a type compatibility test that requires an
arbitrary non-constant expression to be evaluated, so in one way it is
not surprising that there is no error message. Unfortunately I can't
square that with the requirement to issue a diagnostic when a pointer to
an incompatible type is passed to a function.
 
I

Ian Collins

Ian Collins said:
On 04/27/11 09:49 AM, Ben Bacarisse wrote:
In addition, the change is very sight. A program that behaves
differently were a VLA to become a plain arrays is already something of
a monster. I can come up with such programs, but they are all very
contrived. There may be a natural example, but it seems unlikely.

I was trying to come up with one and the first example I conjured up
surprised me:

typedef char C[16];

void f( C* c ) {}

int main(void)
{
char c[16];

f(&c );

const unsigned n = 15;

char cc[n];

f(&cc );
}

Compiles fine as c99 (which was the surprise),

Hmm... At first glance it looks like a diagnostic is required. 6.7.5.2
p6 says:

"For two array types to be compatible, both shall have compatible
element types, and if both size specifiers are present, and are
integer constant expressions, then both size specifiers shall have the
same constant value. 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."

so are '&cc' and the parameter 'c' pointers to compatible types? This
paragraph does not say. Maybe it is specified somewhere else, but I
can't find it.

Me neither, that's what surprised me.
I think the intent of the last sentence is to make the program's
behaviour is undefined, but that would imply the need for a diagnostic.
but fails as C++ (type
mismatch). What did surprise me was the VLA type matching even though
the size differed. Changing the declaration of cc to cc[15] did
produce the expected compiler warnings.

The standard can't mandate a type compatibility test that requires an
arbitrary non-constant expression to be evaluated, so in one way it is
not surprising that there is no error message. Unfortunately I can't
square that with the requirement to issue a diagnostic when a pointer to
an incompatible type is passed to a function.

I agree. I tried the code with both gcc 4.5.1 (-ansi -Wall -pedantic
-std=c99) and Sun c99.

I thought a VLA would always have an incompatible type, given its size
isn't known at compile time. In cases like this, I miss C++'s typeid!
 
K

Keith Thompson

Ian Collins said:
Ian Collins said:
On 04/27/11 09:49 AM, Ben Bacarisse wrote:
In addition, the change is very sight. A program that behaves
differently were a VLA to become a plain arrays is already something of
a monster. I can come up with such programs, but they are all very
contrived. There may be a natural example, but it seems unlikely.

I was trying to come up with one and the first example I conjured up
surprised me:

typedef char C[16];

void f( C* c ) {}

int main(void)
{
char c[16];

f(&c );

const unsigned n = 15;

char cc[n];

f(&cc );
}

Compiles fine as c99 (which was the surprise),

Hmm... At first glance it looks like a diagnostic is required. 6.7.5.2
p6 says:

"For two array types to be compatible, both shall have compatible
element types, and if both size specifiers are present, and are
integer constant expressions, then both size specifiers shall have the
same constant value. 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."

so are '&cc' and the parameter 'c' pointers to compatible types? This
paragraph does not say. Maybe it is specified somewhere else, but I
can't find it.

Me neither, that's what surprised me.
I think the intent of the last sentence is to make the program's
behaviour is undefined, but that would imply the need for a diagnostic.
but fails as C++ (type
mismatch). What did surprise me was the VLA type matching even though
the size differed. Changing the declaration of cc to cc[15] did
produce the expected compiler warnings.

The standard can't mandate a type compatibility test that requires an
arbitrary non-constant expression to be evaluated, so in one way it is
not surprising that there is no error message. Unfortunately I can't
square that with the requirement to issue a diagnostic when a pointer to
an incompatible type is passed to a function.

I agree. I tried the code with both gcc 4.5.1 (-ansi -Wall -pedantic
-std=c99) and Sun c99.

I thought a VLA would always have an incompatible type, given its size
isn't known at compile time. In cases like this, I miss C++'s typeid!

C99 6.7.5.2p6:

For two array types to be compatible, both shall have compatible
element types, and if both size specifiers are present, and are
integer constant expressions, then both size specifiers shall
have the same constant value. 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.
 

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,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top