multidimensional heap arrays

I

Ian Collins

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.

Ah, so they are incompatible, but no diagnostic was issued by either
compiler. I'd be interested in seeing if another C99 compiler issues one.
 
T

Tim Rentsch

Ben Bacarisse said:
Ian Collins said:
[snip] I was trying to come up [an example where the VLA-ness
changes the semantics]:

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.

No diagnostic is required because the last sentence isn't a
constraint. That is, it isn't a restriction on the language
(does not have 'shall' or 'shall not').

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.

The VLA type is compatible with the non-VLA type, per the first
sentence of 6.7.5.2p6. The second sentence doesn't affect
the compatibility relationship, just the defined-ness of
runtime behavior.
 
T

Tim Rentsch

Ian Collins said:
Ah, so they are incompatible, but no diagnostic was issued by either
compiler. I'd be interested in seeing if another C99 compiler issues
one.

No, a VLA type is compatible with a non-VLA array type if they
have compatible element types. The second sentence of 6.7.5.2p6
doesn't affect compatibility.
 
I

Ian Collins

No, a VLA type is compatible with a non-VLA array type if they
have compatible element types. The second sentence of 6.7.5.2p6
doesn't affect compatibility.

It doesn't affect compatibility, but surly passing the address of one to
a function expecting a pointer to a fixed size array is "used in a
context which requires them to be compatible"?

Both compilers issue a diagnostic if the size specifier is not present, ie:

extern int x[];

f( &x );
 
B

Ben Bacarisse

Tim Rentsch said:
Ben Bacarisse said:
Ian Collins said:
[snip] I was trying to come up [an example where the VLA-ness
changes the semantics]:

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.

No diagnostic is required because the last sentence isn't a
constraint. That is, it isn't a restriction on the language
(does not have 'shall' or 'shall not').

Yes, I know, but that was not my point. I was saying that if the intent
was for the behaviour to be undefined (due to that last sentence) it
would be because the two types are incompatible and that incompatibility
would require a diagnostic form the call. However, I can see that is
not what is written. What it says is simply that unequal sizes gives
rise to UB, not to incompatibility. So thank you for making me read it
again ore carefully.
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.

The VLA type is compatible with the non-VLA type, per the first
sentence of 6.7.5.2p6.

Oh, I see. I am amazed at how differently I read it. I think it would
be clearer as two sentences:

"For two array types to be compatible, both shall have compatible
element types. If both size specifiers are present, and are integer
constant expressions, then both size specifiers shall have the same
constant value."

but it's late, and no doubt that carries some other unintended meaning.
The second sentence doesn't affect
the compatibility relationship, just the defined-ness of
runtime behavior.

Yes, I see that now.
 
B

Ben Bacarisse

Keith Thompson said:
Ben Bacarisse said:
Keith Thompson said:
Ian Collins <[email protected]> writes:
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?

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

At least in this case no program can tell if anything is or it not
evaluated.
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.


I don't think it such a dependence is prohibited (as it is for pointers
to structs for example).

Do you think there is something wrong with the very type itself when 'i'
is indeterminate?
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''.

I've always taken it to mean that, but not based on any real evidence
from the standard. I don't think the notion is well-defined at all.
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.

That looks good to me. Of course it would change the meaning of the
code I posted but that's a good thing in this case.
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?

I can't see any advantage in doing that, but it's way too late here for
thinking about such things.
 
K

Keith Thompson

Ian Collins said:
Ah, so they are incompatible, but no diagnostic was issued by either
compiler. I'd be interested in seeing if another C99 compiler issues
one.

It's some rather odd wording. The types are compatible, but using them
in a context that requires compatibility is undefined behavior.

It makes sense, or at least the consequences do. Type compatibility is
purely a compile-time issue; VLAs introduce cases where compatible types
can't be used together, but in a way that the error can't (generally) be
detected at compile time.
 
T

Tim Rentsch

Ian Collins said:
It doesn't affect compatibility, but surly passing the address of one
to a function expecting a pointer to a fixed size array is "used in a
context which requires them to be compatible"?

Yes, and that results in undefined behavior, but since compatibility
isn't affected there is no constraint violation, so no diagnostic
is required.

Both compilers issue a diagnostic if the size specifier is not present, ie:

extern int x[];

f( &x );

This surprises me (at least for C). The types 'int[]' and 'int[N]'
for any integer constant expression N are compatible types, so
pointers to them are compatible types. Is the diagnostic a warning?
Might it have been provoked by use of -Wall, or something similar?
 
T

Tim Rentsch

Ben Bacarisse said:
Tim Rentsch said:
Ben Bacarisse said:
[snip] I was trying to come up [an example where the VLA-ness
changes the semantics]:

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.

No diagnostic is required because the last sentence isn't a
constraint. That is, it isn't a restriction on the language
(does not have 'shall' or 'shall not').

Yes, I know, but that was not my point. I was saying that if the intent
was for the behaviour to be undefined (due to that last sentence) it
would be because the two types are incompatible and that incompatibility
would require a diagnostic form the call.

Ahh, I see. I misunderstood your intended implication there.
However, I can see that is
not what is written. What it says is simply that unequal sizes gives
rise to UB, not to incompatibility. So thank you for making me read it
again ore carefully.

You are most welcome, sir.

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.

The VLA type is compatible with the non-VLA type, per the first
sentence of 6.7.5.2p6.

Oh, I see. I am amazed at how differently I read it. I think it would
be clearer as two sentences:

"For two array types to be compatible, both shall have compatible
element types. If both size specifiers are present, and are integer
constant expressions, then both size specifiers shall have the same
constant value."

but it's late, and no doubt that carries some other unintended meaning.

As written, one problem is the second sentence is (arguably)
decoupled from the definition of compatibility, so its 'shall'
again just causes UB on length mismatches rather than forcing
a diagnostic. Writing Standard-suitable text is a lot harder
than it looks.
 
I

Ian Collins

Yes, and that results in undefined behavior, but since compatibility
isn't affected there is no constraint violation, so no diagnostic
is required.

An interesting defect in the language. Or have I become too used to the
extra type safety in C++?
Both compilers issue a diagnostic if the size specifier is not present, ie:

extern int x[];

f(&x );

This surprises me (at least for C). The types 'int[]' and 'int[N]'
for any integer constant expression N are compatible types, so
pointers to them are compatible types. Is the diagnostic a warning?
Might it have been provoked by use of -Wall, or something similar?

A warning in both, Sun c99 with no options, gcc with -std=c99.
 
I

Ian Collins

Both compilers issue a diagnostic if the size specifier is not present, ie:

extern int x[];

f(&x );

This surprises me (at least for C). The types 'int[]' and 'int[N]'
for any integer constant expression N are compatible types, so
pointers to them are compatible types. Is the diagnostic a warning?
Might it have been provoked by use of -Wall, or something similar?

A warning in both, Sun c99 with no options, gcc with -std=c99.

Silly me. Notice I wrote extern int x[], not extern char x[].
 

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

Forum statistics

Threads
474,085
Messages
2,570,597
Members
47,220
Latest member
AugustinaJ

Latest Threads

Top