Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #73526 > unrolled thread
| Started by | supercat@casperkitty.com |
|---|---|
| First post | 2015-10-21 16:47 -0700 |
| Last post | 2015-10-28 16:37 -0700 |
| Articles | 20 on this page of 38 — 6 participants |
Back to article view | Back to comp.lang.c
How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-10-21 16:47 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-24 21:20 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-10-25 11:44 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-28 12:15 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-10-28 14:25 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Keith Thompson <kst-u@mib.org> - 2015-10-28 16:23 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-28 17:08 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Keith Thompson <kst-u@mib.org> - 2015-10-28 18:09 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-10-28 20:02 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Bartc <bc@freeuk.com> - 2015-10-29 10:55 +0000
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-10-29 06:50 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Keith Thompson <kst-u@mib.org> - 2015-10-29 08:52 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-10-31 10:21 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Keith Thompson <kst-u@mib.org> - 2015-10-29 08:50 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-10-29 12:25 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-30 13:02 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Keith Thompson <kst-u@mib.org> - 2015-10-30 13:35 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-30 19:57 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Keith Thompson <kst-u@mib.org> - 2015-10-31 12:27 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Eric Sosman <esosman@comcast-dot-net.invalid> - 2015-10-30 17:02 -0400
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Keith Thompson <kst-u@mib.org> - 2015-10-30 14:16 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-10-31 08:13 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-31 08:42 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-10-31 10:40 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-11-02 01:45 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-11-02 06:57 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-11-02 08:12 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-11-02 08:54 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-11-04 07:24 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-11-04 09:22 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-11-04 19:48 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-11-05 09:44 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-11-07 10:17 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data raltbos@xs4all.nl (Richard Bos) - 2015-11-10 15:37 +0000
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data supercat@casperkitty.com - 2015-11-10 09:01 -0800
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-28 16:48 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Keith Thompson <kst-u@mib.org> - 2015-10-28 16:11 -0700
Re: How does strict aliasing work with parallel arrays in a union or const-initialized data Tim Rentsch <txr@alumni.caltech.edu> - 2015-10-28 16:37 -0700
Page 1 of 2 [1] 2 Next page →
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-21 16:47 -0700 |
| Subject | How does strict aliasing work with parallel arrays in a union or const-initialized data |
| Message-ID | <44332515-f0c2-40f4-b435-c1351188e3bc@googlegroups.com> |
Given the following programs
// FIRST PROGRAM -- PARALLEL ARRAYS
typedef union {
uint8_t bb[8];
uint16_t hh[4];
uint32_t ww[2];
uint64_t l;
} OCTABYTE;
OCTABYTE thing;
int main(void)
{
thing.ww[0] = 0x12341234;
printf("%X ", (unsigned)thing.hh[0]);
return 0;
}
// SECOND PROGRAM -- CONST INITIALIZED DATA
typedef struct {
uint16_t command;
uint16_t num_params;
uint8_t params[];
} COMMAND_WITH_PARAMS;
const struct {
uint16_t command;
uint16_t num_params;
uint8_t params[4];
} startup_command = { 23, 4, {1,2,3,4} };
COMMAND_WITH_PARAMS const *cmd = (COMMAND_WITH_PARAMS const*)startup_command;
int main(void)
{
printf("%X %X", (unsigned)thing.hh[0], (unsigned)(cmd->num_params));
return 0;
}
would their behavior be defied or undefined in C99?
The first program reads data which was written as a different type, but
I'm not clear on whether the read and/or write are deemed to go through
the union, or if the write takes the address of thing.ww, regards it as
a "uint32_t*" which has nothing to do with "thing", and stores the data,
and the read then takes the address of thing.hh, regards it as a "uint16_t*"
which has nothing to do with "thing", and tries to read it. Is there
anything in the Standard to make that clear?
The second program reads data which was never written at all once main
started operation (and thus shouldn't pose any aliasing problems) but
whose initialization type is a structure that can't be the same as the
type used to read it (since C99 doesn't a structure containing a Flexible
Array Member to be initialized). Is there anything in the Standard to
make clear whether that is permitted or not permitted, and if it isn't
permitted, is there any way to create statically-initialized data which
can be used as a structure with a Flexible Array Member?
If the rules are as clear as some people suggest, hopefully someone can
shed some light on how it would behave given the above examples.
[toc] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-10-24 21:20 -0700 |
| Message-ID | <kfn4mhfefyv.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #73526 |
supercat@casperkitty.com writes:
[need #include for <stdint.h> and {stdio.h>]
>
> typedef union {
> uint8_t bb[8];
> uint16_t hh[4];
> uint32_t ww[2];
> uint64_t l;
> } OCTABYTE;
>
> OCTABYTE thing;
>
> int main(void)
> {
> thing.ww[0] = 0x12341234;
>
> printf("%X ", (unsigned)thing.hh[0]);
> return 0;
> }
Behavior is defined; it is implementation-dependent because of
how the different types are represented, but modulo that it is
defined. (In all cases the remarks are made for C99.)
Incidentally, the term used in the Standard regarding aliasing
issues is "effective type"; there is no mention or use of the
term "strict aliasing", which IIANM is a gcc-ism.
[need #include for <stdint.h> and {stdio.h>]
>
> typedef struct {
> uint16_t command;
> uint16_t num_params;
> uint8_t params[];
> } COMMAND_WITH_PARAMS;
>
> const struct {
> uint16_t command;
> uint16_t num_params;
> uint8_t params[4];
> } startup_command = { 23, 4, {1,2,3,4} };
>
> COMMAND_WITH_PARAMS const *cmd = (COMMAND_WITH_PARAMS const*)startup_command;
>
> int main(void)
> {
> printf("%X %X", (unsigned)thing.hh[0], (unsigned)(cmd->num_params));
> return 0;
> }
Even assuming the pointer conversion doesn't have an alignment
problem, the access 'cmd->num_params' is still undefined
behavior.
> The first program reads data which was written as a different
> type, but I'm not clear on whether the read and/or write are
> deemed to go through the union, or if the write takes the address
> of thing.ww, regards it as a "uint32_t*" which has nothing to do
> with "thing", and stores the data, and the read then takes the
> address of thing.hh, regards it as a "uint16_t*" which has nothing
> to do with "thing", and tries to read it. Is there anything in
> the Standard to make that clear?
>
> The second program reads data which was never written at all once
> main started operation (and thus shouldn't pose any aliasing
> problems) but whose initialization type is a structure that can't
> be the same as the type used to read it (since C99 doesn't a
> structure containing a Flexible Array Member to be initialized).
> Is there anything in the Standard to make clear whether that is
> permitted or not permitted,
Assigning a meaning to English text is inherently a subjective
process. IMO the Standard is clear in providing answers to both
of these questions, but I admit the possibility that other people
may have a different opinion in one or both cases. Is what
you're looking just a statement of opinion, or are you looking
for something more than that?
> and if it isn't permitted, is there any way to create
> statically-initialized data which can be used as a structure
> with a Flexible Array Member?
Yes. Put a non-FAM struct type and a FAM struct type in a union,
and declare an instance of the union type, initializing the
non-FAM struct type member of the union, but using the FAM struct
type member. Note that the complete union type (ie, including
definition of its members) must be visible at every point of
access. (The non-FAM struct type is just like the FAM struct
type, except it has a regular array rather than a FAM for
the last member.)
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-25 11:44 -0700 |
| Message-ID | <6408f9ec-e8d2-4526-96ca-271e1d201484@googlegroups.com> |
| In reply to | #73751 |
On Saturday, October 24, 2015 at 11:21:21 PM UTC-5, Tim Rentsch wrote:
> supercat writes:
> > thing.ww[0] = 0x12341234;
> > printf("%X ", (unsigned)thing.hh[0]);
> Behavior is defined; it is implementation-dependent because of
> how the different types are represented, but modulo that it is
> defined. (In all cases the remarks are made for C99.)
Is there anything specific in the standard that makes clear that
thing.ww[0] = 0x12341234; is processed as a write to "thing", rather
than regarding "thing.ww" as an expression which yields a pointer
which is then offset and dereferenced without reference to the object
of which it had been a part? Certainly if the code had been:
uint32_t *wp = thing.ww+0;
*wp = 0x12341234;
uint16_t *hp = thing.hh+0;
printf("%04X", (unsigned)(*hp));
that would violate the effective-type rules, but my impression is that for
all purposes *other* than the effective type rules the meaning of
"thing.ww[0]" would be equivalent to the code given above.
> Incidentally, the term used in the Standard regarding aliasing
> issues is "effective type"; there is no mention or use of the
> term "strict aliasing", which IIANM is a gcc-ism.
While gcc may be the first compiler to apply the specific term, I think
others have adopted the terminology, and since the section number which
applies the rule is different in C89 and C99 (and perhaps C11 and C14
as well) I don't really know what "title" would be best to refer to it.
> > typedef struct {
> > uint16_t command;
> > uint16_t num_params;
> > uint8_t params[];
> > } COMMAND_WITH_PARAMS;
> >
> > const struct {
> > uint16_t command;
> > uint16_t num_params;
> > uint8_t params[4];
> > } startup_command = { 23, 4, {1,2,3,4} };
> Even assuming the pointer conversion doesn't have an alignment
> problem, the access 'cmd->num_params' is still undefined
> behavior.
Is there any way there could be an alignment problem? The structures
contain elements of the same types, and I believe the C Standard mandated
that if two structures start with elements of the same types, the leading
portion of the structures that matches will have the same representation.
Am I missing something?
> Assigning a meaning to English text is inherently a subjective
> process. IMO the Standard is clear in providing answers to both
> of these questions, but I admit the possibility that other people
> may have a different opinion in one or both cases. Is what
> you're looking just a statement of opinion, or are you looking
> for something more than that?
It would be nice to know if there's something in some defect report,
rationale, or other official document which would clarify the issue.
> > and if it isn't permitted, is there any way to create
> > statically-initialized data which can be used as a structure
> > with a Flexible Array Member?
>
> Yes. Put a non-FAM struct type and a FAM struct type in a union,
> and declare an instance of the union type, initializing the
> non-FAM struct type member of the union, but using the FAM struct
> type member. Note that the complete union type (ie, including
> definition of its members) must be visible at every point of
> access. (The non-FAM struct type is just like the FAM struct
> type, except it has a regular array rather than a FAM for
> the last member.)
Do any versions of the C Standard allow a structure containing an FAM type
to appear within a union? GCC allows that as an extension, but the C99
compiler I use won't allow that unless gcc extensions are enabled, and if
I'm going to limit myself to using particular compilers I may as well just
specify that the code must be compiled with that mode selection disabled.
I don't think it would be practical to say that every point of access must
be done through the union type, since that would require all code which
makes use of the type to know all of the sizes of object which are going
to be created with static duration; since in some cases I might be using
that pattern with things that are hundreds of items long, declaring a whole
fleet of "Thing of size 1", "Thing of size 2", etc. types seems a bit
impractical.
If I didn't need static initialization, I could simply declare a variable of
the latter type but then only use it via a pointer of the FAM-based type
and that would be guaranteed to work (since the variable would never be
read as any type other than that via which it was written). Further, given
the intended purpose of the "effective type" rule, I can't realistically
imagine that any compiler which was using the rule only for that purpose
would care about the effective type with which statically-initialized data
is written (since there's no way any compiler is going to want to keep that
data in registers). Still, if there were a nice way to write the code in
compliance with the standard, it would seem desirable to do so.
As it is, I think that should probably be regarded as the sort of code which
was never supposed to have been forbidden (since its semantic effect really
cannot be accomplished via any other means), figure that the authors of C89
probably expected that compilers would always do the right thing with it,
and hope that I never get stuck with a compiler that tries to get too
"creative". From what I understand, C11 weakened the rules to the point
that the above code would be legal since the two structures have a member
in common, so the only danger would be with a compiler that pushes the edge
of optimizations but doesn't support C11, so maybe I'm pretty safe.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-10-28 12:15 -0700 |
| Message-ID | <kfn611qdcu2.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #73778 |
supercat@casperkitty.com writes:
> On Saturday, October 24, 2015 at 11:21:21 PM UTC-5, Tim Rentsch wrote:
>> supercat writes [reordred just slightly]:
>> Incidentally, the term used in the Standard regarding aliasing
>> issues is "effective type"; there is no mention or use of the
>> term "strict aliasing", which IIANM is a gcc-ism.
>
> While gcc may be the first compiler to apply the specific term, I think
> others have adopted the terminology, and since the section number which
> applies the rule is different in C89 and C99 (and perhaps C11 and C14
> as well) I don't really know what "title" would be best to refer to it.
The problem with "strict aliasing" is that no one really knows
what the term means exactly. If asked a question involving what
"strict aliasing" allows, the only accurate answer one can give
is "I'm not sure". If what you're asking about is what ISO C
says about certain questions, it is better to use the terms
defined in the Standard, so there is no confusion about what
question is being asked.
>>> thing.ww[0] = 0x12341234;
>>> printf("%X ", (unsigned)thing.hh[0]);
>>
>> Behavior is defined; it is implementation-dependent because of
>> how the different types are represented, but modulo that it is
>> defined. (In all cases the remarks are made for C99.)
>
> Is there anything specific in the standard that makes clear that
> thing.ww[0] = 0x12341234; is processed as a write to "thing", rather
> than regarding "thing.ww" as an expression which yields a pointer
> which is then offset and dereferenced without reference to the object
> of which it had been a part?
There is no way to give an objective answer to this question.
What is it that are you trying to find out?
> Certainly if the code had been:
>
> uint32_t *wp = thing.ww+0;
> *wp = 0x12341234;
> uint16_t *hp = thing.hh+0;
> printf("%04X", (unsigned)(*hp));
>
> that would violate the effective-type rules,
Oh? Why do you think that?
> but my impression is that for
> all purposes *other* than the effective type rules the meaning of
> "thing.ww[0]" would be equivalent to the code given above.
I think you're equating "effective type" and any part of the
Standard that affects matters of aliasing. The two are not
the same.
>>> typedef struct {
>>> uint16_t command;
>>> uint16_t num_params;
>>> uint8_t params[];
>>> } COMMAND_WITH_PARAMS;
>>>
>>> const struct {
>>> uint16_t command;
>>> uint16_t num_params;
>>> uint8_t params[4];
>>> } startup_command = { 23, 4, {1,2,3,4} };
>>
>> Even assuming the pointer conversion doesn't have an alignment
>> problem, the access 'cmd->num_params' is still undefined
>> behavior.
>
> Is there any way there could be an alignment problem?
Yes.
> The structures
> contain elements of the same types, and I believe the C Standard mandated
> that if two structures start with elements of the same types, the leading
> portion of the structures that matches will have the same representation.
> Am I missing something?
What I think you're missing is that the "special guarantee" given
in section 6.5.2.3 applies /only/ when the struct objects are
contained in an encompassing union object. Here that is not
the case. There is no reason the alignment of the standalone
struct has to match the alignment of the same struct when in
a union. It could, but it doesn't have to.
>> Assigning a meaning to English text is inherently a subjective
>> process. IMO the Standard is clear in providing answers to both
>> of these questions, but I admit the possibility that other people
>> may have a different opinion in one or both cases. Is what
>> you're looking just a statement of opinion, or are you looking
>> for something more than that?
>
> It would be nice to know if there's something in some defect report,
> rationale, or other official document which would clarify the issue.
That's true. They are available for your perusal at
http://www.open-std.org/jtc1/sc22/wg14/www/docs
>>> and if it isn't permitted, is there any way to create
>>> statically-initialized data which can be used as a structure
>>> with a Flexible Array Member?
>>
>> Yes. Put a non-FAM struct type and a FAM struct type in a union,
>> and declare an instance of the union type, initializing the
>> non-FAM struct type member of the union, but using the FAM struct
>> type member. Note that the complete union type (ie, including
>> definition of its members) must be visible at every point of
>> access. (The non-FAM struct type is just like the FAM struct
>> type, except it has a regular array rather than a FAM for
>> the last member.)
>
> Do any versions of the C Standard allow a structure containing an
> FAM type to appear within a union? GCC allows that as an
> extension, but the C99 compiler I use won't allow that unless gcc
> extensions are enabled, [snip]
What makes you think C99 (or later) does not allow declaring
of struct-wth-FAM objects, either as ordinary identfiers or
as struct/union members? AFAIK they are allowed. When I tried
compiling a simple test case with gcc, using -std=c99 -pedantic,
no diagnostics were given. So I'm at a loss to understand your
question here.
> I don't think it would be practical to say that every point of
> access must be done through the union type, [snip]
That isn't the rule. The completed definition of the union /type/
must be visible at the point of access, but access can be done
through any of the struct types directly contained in the union (ie,
for members in the common initial sequence). Please read or re-read
the "one special guarantee" paragraph given in section 6.5.2.3,
along with the long example given at the end of that section.
Ultimately if you want really good answers to these questions you
will need to become familiar with exactly what the Standard says
about them, not just what other people say it says.
> If I didn't need static initialization, I could simply [snip].
> Still, if there were a nice way to write the code in compliance
> with the standard, it would seem desirable to do so.
To the best of my understanding it is fairly easy to do what you
want and still have code that has defined behavior. Perhaps
rather tedious, but still fairly easy.
> As it is, I think that should probably be regarded as the sort of
> code which was never supposed to have been forbidden (since its
> semantic effect really cannot be accomplished via any other
> means), figure that the authors of C89 probably expected that
> compilers would always do the right thing with it, and hope that I
> never get stuck with a compiler that tries to get too "creative".
IMO this is a bad assumption, both abstractly and in practice.
Moreover an unnecessary one, in light of the above comments.
> From what I understand, C11 weakened the rules to the point that
> the above code would be legal since the two structures have a
> member in common, so the only danger would be with a compiler that
> pushes the edge of optimizations but doesn't support C11, so maybe
> I'm pretty safe.
What makes you say C11 is different from C99 about this? AFAIK
there is no difference between the two versions in this respect
(not counting atomic types, which didn't exist in C99).
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-28 14:25 -0700 |
| Message-ID | <b49266e4-9532-4179-9d4c-fd2c02d7236b@googlegroups.com> |
| In reply to | #74099 |
On Wednesday, October 28, 2015 at 2:15:29 PM UTC-5, Tim Rentsch wrote:
> supercat writes:
> > Is there anything specific in the standard that makes clear that
> > thing.ww[0] = 0x12341234; is processed as a write to "thing", rather
> > than regarding "thing.ww" as an expression which yields a pointer
> > which is then offset and dereferenced without reference to the object
> > of which it had been a part?
>
> There is no way to give an objective answer to this question.
> What is it that are you trying to find out?
I guess I should start by recognizing that C99 and C11 have rules that
define things differently; I'd like to have code be compatible with both
when practical, though it would be useful to know in what ways C11 fixes
shortcomings of the C99 rules, and to what extent it adds shortcomings
of its own.
In a complicated expression, what is meant by "reading" or "writing"
as some particular type, and in what cases would the decay of arrays into
pointers affect that? Normally, the expression thing.ww[0] would mean
"take the address of the first uint32_t in the array thing.ww, add 0 to it,
and then dereference the thing identified by that uint32_t*. Is the write
to that uint32_t* somehow also a regarded as a write to the containing
union type?
> > Certainly if the code had been:
> >
> > uint32_t *wp = thing.ww+0;
> > *wp = 0x12341234;
> > uint16_t *hp = thing.hh+0;
> > printf("%04X", (unsigned)(*hp));
> >
> > that would violate the effective-type rules,
>
> Oh? Why do you think that?
Because the write to wp is would make the storage hold a uint32_t, and the
read of hp would try to read it as a uint16_t, and either uint32_t nor
uint16_t is struct or union which contains the other as a member.
I suppose under C11's effective type rules, that might be legitimate, though
from what I can tell C11 adds new problems of its own.
> > but my impression is that for
> > all purposes *other* than the effective type rules the meaning of
> > "thing.ww[0]" would be equivalent to the code given above.
>
> I think you're equating "effective type" and any part of the
> Standard that affects matters of aliasing. The two are not
> the same.
What would make thing.ww[0] different from *wp for purposes of the
rules regarding either if the enclosing object were something of heap
duration?
> >>> typedef struct {
> >>> uint16_t command;
> >>> uint16_t num_params;
> >>> uint8_t params[];
> >>> } COMMAND_WITH_PARAMS;
> >>>
> >>> const struct {
> >>> uint16_t command;
> >>> uint16_t num_params;
> >>> uint8_t params[4];
> >>> } startup_command = { 23, 4, {1,2,3,4} };
> >>
> >> Even assuming the pointer conversion doesn't have an alignment
> >> problem, the access 'cmd->num_params' is still undefined
> >> behavior.
> >
> > Is there any way there could be an alignment problem?
>
> Yes.
>
> > The structures
> > contain elements of the same types, and I believe the C Standard mandated
> > that if two structures start with elements of the same types, the leading
> > portion of the structures that matches will have the same representation.
> > Am I missing something?
>
> What I think you're missing is that the "special guarantee" given
> in section 6.5.2.3 applies /only/ when the struct objects are
> contained in an encompassing union object. Here that is not
> the case. There is no reason the alignment of the standalone
> struct has to match the alignment of the same struct when in
> a union. It could, but it doesn't have to.
As I wrote the code, neither structure is in a union. It would often
be expected that the alignment of a union which contains a struct FOO
will be coarser than that of struct FOO (e.g. if the union contains
any types with coarser alignment), but neither of the structs I showed
was in a union, and they both had members that were identical except
that one had a flexible array member and the other had a fixed-sized
array.
If I have one translation unit which contains the definitions:
struct FOO { int x,y; char b[]; };
struct GOO { int x,y; char b[4]; };
and another that contains those two definitions plus
union FOOGOO { FOO f; GOO g; };
the layout of the FOO in both translation units must be identical, the
layout of GOO in both translation units must be identical. Since the
layouts of common initial sequences of structure types FOO and GOO in
the second unit must be identical, would that not imply the same about
those in the first?
> What makes you think C99 (or later) does not allow declaring
> of struct-wth-FAM objects, either as ordinary identfiers or
> as struct/union members? AFAIK they are allowed. When I tried
> compiling a simple test case with gcc, using -std=c99 -pedantic,
> no diagnostics were given. So I'm at a loss to understand your
> question here.
The Keil ARM compiler version 5 has a "C99" and GCC modes. Without C99
mode enabled, it won't accept flexible-array member syntax at all.
With C99 mode, it accepts flexible-array member syntax, but unless
GCC mode is enabled a struct containing a flexible array member may not
be included as part of a union nor as anything other than the last member
of another struct [in which case the latter struct is treated as having a
flexible array member]. That GCC allows the code isn't surprising, since
Keil in GCC mode does as well. Why do you suppose Keil went through the
trouble of restricting how FAM structs can be used in non-GCC mode if
the standard regarded such structures as complete allocatable types?
> > I don't think it would be practical to say that every point of
> > access must be done through the union type, [snip]
>
> That isn't the rule. The completed definition of the union /type/
> must be visible at the point of access, but access can be done
> through any of the struct types directly contained in the union (ie,
> for members in the common initial sequence). Please read or re-read
> the "one special guarantee" paragraph given in section 6.5.2.3,
> along with the long example given at the end of that section.
> Ultimately if you want really good answers to these questions you
> will need to become familiar with exactly what the Standard says
> about them, not just what other people say it says.
I just read that part of the Standard, and would regard their examples as
poor, since in the "good" example the object is being accessed *as* the
union type, and in the "bad" example the union type is not visible at the
point where the object is being accessed.
If the intention is that the existence of a union type containing types
P and Q will allow code to use a pointer to type P as a pointer to type Q,
that would make things somewhat less unworkable than they otherwise would
be, but there's still a problem of how to have the code which expects to
receive a pointer to the struct with the FAM accept a pointer to a struct
stored as static constant data. Since one of the places where I use that
pattern (fortunately with a compiler that doesn't care about aliasing rules)
is on a micro with 3.9K of RAM, and since some the structures in question
are larger than that, copying the structures to heap storage does not seem
like a practical solution.
> > If I didn't need static initialization, I could simply [snip].
> > Still, if there were a nice way to write the code in compliance
> > with the standard, it would seem desirable to do so.
>
> To the best of my understanding it is fairly easy to do what you
> want and still have code that has defined behavior. Perhaps
> rather tedious, but still fairly easy.
Is the ability to allocate elements containing flexible array members a
GCC-ism, or is it part of the Standard? If it's part of the Standard, why
would some compilers forbid it (I've heard of the restrictions long before
using Keil)?
> > As it is, I think that should probably be regarded as the sort of
> > code which was never supposed to have been forbidden (since its
> > semantic effect really cannot be accomplished via any other
> > means), figure that the authors of C89 probably expected that
> > compilers would always do the right thing with it, and hope that I
> > never get stuck with a compiler that tries to get too "creative".
>
> IMO this is a bad assumption, both abstractly and in practice.
> Moreover an unnecessary one, in light of the above comments.
The coverage of flexible-array members in N1570 seems pretty inadequate; it does not, for example, say that the reported size of a structure with a flexible array member as its last element must be at least equal to the offset of that member within the structure, though I can't see how anyone could be expected to write sane code without such a guarantee.
Further, flexible array members certainly were not available in C89 when the
aliasing rules were first written; by what means could one write code to meet
the indicated requirement in C89 without going against the aliasing rules?
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-10-28 16:23 -0700 |
| Message-ID | <lnlhambmrj.fsf@kst-u.example.com> |
| In reply to | #74106 |
supercat@casperkitty.com writes:
> On Wednesday, October 28, 2015 at 2:15:29 PM UTC-5, Tim Rentsch wrote:
>> supercat writes:
>> > Is there anything specific in the standard that makes clear that
>> > thing.ww[0] = 0x12341234; is processed as a write to "thing", rather
>> > than regarding "thing.ww" as an expression which yields a pointer
>> > which is then offset and dereferenced without reference to the object
>> > of which it had been a part?
>>
>> There is no way to give an objective answer to this question.
>> What is it that are you trying to find out?
>
> I guess I should start by recognizing that C99 and C11 have rules that
> define things differently; I'd like to have code be compatible with both
> when practical, though it would be useful to know in what ways C11 fixes
> shortcomings of the C99 rules, and to what extent it adds shortcomings
> of its own.
You assert that the rules in C11 differ from the rules in C99. In the
area of "strict aliasing" and/or effective type rules, I don't believe
that's correct, though I could easily be missing something. What
differences do you think there are?
[...]
> If I have one translation unit which contains the definitions:
>
> struct FOO { int x,y; char b[]; };
> struct GOO { int x,y; char b[4]; };
>
> and another that contains those two definitions plus
>
> union FOOGOO { FOO f; GOO g; };
>
> the layout of the FOO in both translation units must be identical, the
> layout of GOO in both translation units must be identical. Since the
> layouts of common initial sequences of structure types FOO and GOO in
> the second unit must be identical, would that not imply the same about
> those in the first?
No. The standard explicitly says that the layouts must be identical in
the second unit. It makes no such guarantee for the first.
The easiest way for a compiler to satisfy the standard's requirements
would be to make the layouts identical in either case, but the standard
doen't require this.
>> What makes you think C99 (or later) does not allow declaring
>> of struct-wth-FAM objects, either as ordinary identfiers or
>> as struct/union members? AFAIK they are allowed. When I tried
>> compiling a simple test case with gcc, using -std=c99 -pedantic,
>> no diagnostics were given. So I'm at a loss to understand your
>> question here.
>
> The Keil ARM compiler version 5 has a "C99" and GCC modes. Without C99
> mode enabled, it won't accept flexible-array member syntax at all.
> With C99 mode, it accepts flexible-array member syntax, but unless
> GCC mode is enabled a struct containing a flexible array member may not
> be included as part of a union nor as anything other than the last member
> of another struct [in which case the latter struct is treated as having a
> flexible array member].
If it accepts a structure containing a structure containing a flexible
array member, even as the last member, it sounds like it's being overly
permissive, since that would be a constraint violation (N1570
6.7.2.1p3).
An example:
struct inner { int len; unsigned char fam[]; };
struct outer { struct inner inn; };
A conforming C11 compiler must issue a diagnostic. (A conforming C90
compiler also must issue a diagnostic, but for a different reason.)
> The coverage of flexible-array members in N1570 seems pretty
> inadequate; it does not, for example, say that the reported size of a
> structure with a flexible array member as its last element must be at
> least equal to the offset of that member within the structure, though
> I can't see how anyone could be expected to write sane code without
> such a guarantee.
It doesn't explicitly say it, but it appears to me that that was the
assumption:
In particular, the size of the structure is as if the flexible array
member were omitted except that it may have more trailing padding
than the omission would imply.
You can use offsetof() to determine where the flexible array member
starts. For example, if you want to allocate a structure with 42
elements allocated to the FAM, you can do something like:
struct_type *ptr = malloc(offsetof(struct_type, fam) +
42 * sizeof (element_type));
(I think that's right; I haven't checked it.)
> Further, flexible array members certainly were not available in C89 when the
> aliasing rules were first written; by what means could one write code to meet
> the indicated requirement in C89 without going against the aliasing rules?
The semantics of accessing elements of a flexible array member are
described in N1570 6.7.2.1p18. I think the description there is enough
to avoid any aliasing problems. What problem do you see?
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-10-28 17:08 -0700 |
| Message-ID | <kfnoafibkpk.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #74116 |
Keith Thompson <kst-u@mib.org> writes:
> supercat@casperkitty.com writes:
>> On Wednesday, October 28, 2015 at 2:15:29 PM UTC-5, Tim Rentsch wrote:
>>> supercat writes:
>>>>[snip]
>>
>> If I have one translation unit which contains the definitions:
>>
>> struct FOO { int x,y; char b[]; };
>> struct GOO { int x,y; char b[4]; };
>>
>> and another that contains those two definitions plus
>>
>> union FOOGOO { FOO f; GOO g; };
>>
>> the layout of the FOO in both translation units must be identical, the
>> layout of GOO in both translation units must be identical. Since the
>> layouts of common initial sequences of structure types FOO and GOO in
>> the second unit must be identical, would that not imply the same about
>> those in the first?
>
> No. The standard explicitly says that the layouts must be identical in
> the second unit. It makes no such guarantee for the first.
I believe it does, indirectly. The struct FOO in TU1 and the
struct FOO in TU2 are compatible types, so they must use the same
layout. Similarly for struct GOO's in TU1 and TU2. Therefore
the two struct types in TU1 must use the same layout, because the
corresponding compatible types might have to in another (unknown)
TU that has those corresponding types in a union.
[snip]
>> The coverage of flexible-array members in N1570 seems pretty
>> inadequate; it does not, for example, say that the reported size of a
>> structure with a flexible array member as its last element must be at
>> least equal to the offset of that member within the structure, though
>> I can't see how anyone could be expected to write sane code without
>> such a guarantee.
>
> It doesn't explicitly say it, but it appears to me that that was the
> assumption:
>
> In particular, the size of the structure is as if the flexible array
> member were omitted except that it may have more trailing padding
> than the omission would imply.
>
> [snip]
Note that there was a change in this area between C99 and N1256.
It might be useful to compare this section in the two documents.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-10-28 18:09 -0700 |
| Message-ID | <lnh9labhuv.fsf@kst-u.example.com> |
| In reply to | #74122 |
Tim Rentsch <txr@alumni.caltech.edu> writes:
> Keith Thompson <kst-u@mib.org> writes:
>
>> supercat@casperkitty.com writes:
>>> On Wednesday, October 28, 2015 at 2:15:29 PM UTC-5, Tim Rentsch wrote:
>>>> supercat writes:
>>>>>[snip]
>>>
>>> If I have one translation unit which contains the definitions:
>>>
>>> struct FOO { int x,y; char b[]; };
>>> struct GOO { int x,y; char b[4]; };
>>>
>>> and another that contains those two definitions plus
>>>
>>> union FOOGOO { FOO f; GOO g; };
[...]
>> No. The standard explicitly says that the layouts must be identical in
>> the second unit. It makes no such guarantee for the first.
>
> I believe it does, indirectly. The struct FOO in TU1 and the
> struct FOO in TU2 are compatible types, so they must use the same
> layout. Similarly for struct GOO's in TU1 and TU2. Therefore
> the two struct types in TU1 must use the same layout, because the
> corresponding compatible types might have to in another (unknown)
> TU that has those corresponding types in a union.
Right. I was thinking of the more general case of two distinct struct
types with a common initial subsequence. I missed the fact that struct
FOO and struct GOO are compatible.
> [snip]
>>> The coverage of flexible-array members in N1570 seems pretty
>>> inadequate; it does not, for example, say that the reported size of a
>>> structure with a flexible array member as its last element must be at
>>> least equal to the offset of that member within the structure, though
>>> I can't see how anyone could be expected to write sane code without
>>> such a guarantee.
>>
>> It doesn't explicitly say it, but it appears to me that that was the
>> assumption:
>>
>> In particular, the size of the structure is as if the flexible array
>> member were omitted except that it may have more trailing padding
>> than the omission would imply.
>>
>> [snip]
>
> Note that there was a change in this area between C99 and N1256.
> It might be useful to compare this section in the two documents.
The change was made in response to Defect Report #282:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_282.htm
Summary:
6.7.2.1 Structure and union specifiers, paragraphs 15 and 16 require
that any padding for alignment of a structure containing a flexible
array member must preceed the flexible array member. This
contradicts existing implementations. We do not believe this was
the intent of the C99 specification.
The original wording in C99 was:
As a special case, the last element of a structure with more
than one named member may have an incomplete array type;
this is called a flexible array member. With two exceptions,
the flexible array member is ignored. First, the size of the
structure shall be equal to the offset of the last element of an
otherwise identical structure that replaces the flexible array
member with an array of unspecified length. Second, when
a . (or ->) operator has a left operand that is (a pointer to)
a structure with a flexible array member and the right operand
names that member, it behaves as if that member were replaced
with the longest array (with the same element type) that would
not make the structure larger than the object being accessed;
the offset of the array shall remain that of the flexible array
member, even if this would differ from that of the replacement
array. If this array would have no elements, it behaves as if it
had one element but the behavior is undefined if any attempt is
made to access that element or to generate a pointer one past it.
with a footnote:
The length is unspecified to allow for the fact that implementations
may give array members different alignments according to their
lengths.
The revised wording in N1256 was:
As a special case, the last element of a structure with more
than one named member may have an incomplete array type;
this is called a flexible array member. In most situations,
the flexible array member is ignored. In particular, the size
of the structure is as if the flexible array member were
omitted except that it may have more trailing padding than
the omission would imply. However, when a . (or ->) operator
has a left operand that is (a pointer to) a structure with a
flexible array member and the right operand names that member,
it behaves as if that member were replaced with the longest
array (with the same element type) that would not make the
structure larger than the object being accessed; the offset of
the array shall remain that of the flexible array member, even
if this would differ from that of the replacement array. If
this array would have no elements, it behaves as if it had one
element but the behavior is undefined if any attempt is made
to access that element or to generate a pointer one past it.
and the footnote was removed.
The same wording appears without change in N1570.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-28 20:02 -0700 |
| Message-ID | <342e8770-35cb-4f9c-ad01-8a660c12d97d@googlegroups.com> |
| In reply to | #74128 |
On Wednesday, October 28, 2015 at 8:09:53 PM UTC-5, Keith Thompson wrote:
> The length is unspecified to allow for the fact that implementations
> may give array members different alignments according to their
> lengths.
Gaaaaaah!
Given the type "struct foo {int64_t x; char y; char b[];} I could see room
for ambiguity as to whether the offset of b should be 9 or 16. The former
is where a normal array would go, but the latter would helpfully make the
array line up with the end of the structure. That behavior wouldn't depend
upon array length, though.
I could see that many optimizations would be possible on with e.g. a
"char[4]" which was word-aligned versus one that wasn't, but at the same
time having something like:
struct q
{
unsigned char cmd;
unsigned char sig[4];
unsigned char x;
}
take eight bytes while
struct q
{
unsigned char cmd;
unsigned char sig[5];
unsigned char x;
}
only took seven would seem like a major violation of the Principle of Least
Astonishment.
I wish C would honor its history as a systems-programming language and let
programmers excercise control over struct layouts. While .NET hardly has
a reputation as a systems-programming framework, it offers much more control
over such things than C does. Obviously if a program demands that a struct
be laid out in a fashion that won't work on a particular system, it should
not compile on such a system, but if a program has no chance of working
unless a structure is laid out a certain way it should be possible to
specify that.
I wonder how many C++ to C translators would have any chance of generating
efficient strictly-portable code in any version of the C Standard?
[toc] | [prev] | [next] | [standalone]
| From | Bartc <bc@freeuk.com> |
|---|---|
| Date | 2015-10-29 10:55 +0000 |
| Message-ID | <n0stru$plq$1@dont-email.me> |
| In reply to | #74136 |
On 29/10/2015 03:02, supercat@casperkitty.com wrote: > I wish C would honor its history as a systems-programming language and let > programmers excercise control over struct layouts. I think you can. You can use something like #pragma pack(1). Then it will use byte alignment for all fields, and it is up to you to space things out by selecting member sizes and inserting padding. But the explicit padding will add extra fields which will affect things like initialisation. (Outside of I've always used byte alignment, which means carefully crafting a struct layout if I care about alignment or overall size. When converting one byte-aligned language to high-level C, then it simplest to turn on #pragma pack(1) and let C lay things out my way, than to try and predict what C will do.) -- Bartc
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-29 06:50 -0700 |
| Message-ID | <563a8df1-7de4-4419-9c87-7f699069bc97@googlegroups.com> |
| In reply to | #74146 |
On Thursday, October 29, 2015 at 5:56:21 AM UTC-5, Bart wrote:
> I think you can.
>
> You can use something like #pragma pack(1). Then it will use byte
> alignment for all fields, and it is up to you to space things out by
> selecting member sizes and inserting padding.
It used to be that an understanding of the C language and an understanding
of a hardware platform would together be sufficient to let one know what
one could do to exploit platform-specific features. With changes in
optimizations that is no longer the case.
I do not consider unreasonable the proposition that if there are five
compilers for the Acme 5000 processor, it should be possible to take code
written for one and feed it to another and have it either work or report
why it cannot do so, and if code seems to work it should be possible to
have confidence that it will work without having to carefully pore through
large amounts of human-readable documentation for both the compiler and
the code.
As it is, the C Standard includes a lot of rules that prevent what would in
many cases be useful optimizations, but at the same time fails to let
programmers lock things down enough to be useful. On the vast majority of
systems, rules for alignment and packing are fairly simple:
Every primitive type has an associated power-of-two alignment
A structure has the alignment of its most-coarsely-aligned member
A union has has the alignment of its most-coarsely-aligned member
An array has the alignment of its constituent type
Structure members are placed at the first location satisfying alignment
Structure sizes are padded out to their alignment
Such rules could be enhanced by allowing code to specify for any type an
alignment which was finer or coarser than normal. This would allow for
optimizations that are not otherwise possible. For example, even if a
compiler eagerly aligns four-byte arrays to 32-bit boundaries, it would
still be legal to pass a method like
void doSomething(char (*arr)[4]);
an arbitrary non-aligned character pointer, and thus such a method would
have to be prepared to accept that possibility. If instead the method
had been written as:
void doSomething(AlignedFourByteArray arr);
then a compiler could squawk at any effort to pass a pointer which was not
known to have 32-bit alignment.
Unless a compiler itself is running on a very limited system, I see no
reason a compiler for any platform shouldn't be able to handle explicit
alignment/layout requests. Compilers for many platforms won't be able to
generate code for certain custom layouts that's nearly as efficient as
code which uses the compiler's preferred layouts, but the compiler-
generated code for such things may still be more efficient than anything
which could be accomplished in readable strictly-conforming C code.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-10-29 08:52 -0700 |
| Message-ID | <lny4elacyv.fsf@kst-u.example.com> |
| In reply to | #74146 |
Bartc <bc@freeuk.com> writes:
> On 29/10/2015 03:02, supercat@casperkitty.com wrote:
>> I wish C would honor its history as a systems-programming language and let
>> programmers excercise control over struct layouts.
>
> I think you can.
>
> You can use something like #pragma pack(1).
Only if your compiler happens to support it.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-31 10:21 -0700 |
| Message-ID | <eb6fc21f-f5d5-4e43-bc6c-1fc8cd4f334f@googlegroups.com> |
| In reply to | #74146 |
On Thursday, October 29, 2015 at 5:56:21 AM UTC-5, Bart wrote:
> On 29/10/2015 03:02, supercat wrote:
>
> > I wish C would honor its history as a systems-programming language and let
> > programmers excercise control over struct layouts.
>
> I think you can.
>
> You can use something like #pragma pack(1). Then it will use byte
> alignment for all fields, and it is up to you to space things out by
> selecting member sizes and inserting padding.
Not only is there no guarantee that any particular compiler will support
"#pragma pack(1)", but there's no guarantee as to what a compiler may do if
it does not support it. By my understanding, a compiler could perfectly
legitimately interpret "#pragma pack(1)" as an indication that it should
ignore everything else in the source file and generate code to solve the
bin packing problem of size 1, or [somewhat less facetiously, actually]
that the compiler should launch the game "Rogue"(*).
What I would like to see would be for the standard to add a new portable
declaration format for bit fields, define a new type syntax, and add
a few more attributes for types. These changes would make it possible
to have code which exploits the memory layout of the processor for which
it is writte, but *still* be portable (albeit possibly not as efficient)
on other processors.
(*) It may be apocryphal, but I've read that there was a version of gcc that
actually did that.
The changes, in order:
BITFIELD DECLARATIONS:
I would propose that the Standard provide a means of declaring that a
bitfield identifier represent an alias to one or more bit ranges within
previously-named fields or array elements, listed in MSB-first order.
Thus, something like:
struct Hey {
unsigned char bb[2];
unsigned value : { bb[0].0:8, bb[1].0:8 }
};
would indicate that reading "Hey.value" should yield
((Hey.bb[0] & 255)<<8) | (Hey.bb[1] & 255)
regardless of the size of "char" or the bit-endianness of the machine. On
machines where "char" is 8 bits, the "& 255" parts could be optimized out;
adding some additional attributes would allow them to be optimized out on
other compilers as well. A compiler for e.g. x86 could further optimize
the code to perform a 16-bit read from the address followed by a byte-swap
instruction (which would be faster than doing two byte reads with a shift
between).
TYPE SYNTAX:
I would add a syntax which says that if the name of a structure or union
is followed by a period and a field name, the resulting type shall have
the storage format of the structure or union, but lvalues and rvalues of
that type shall refer to the underlying field. All such types based on
the same struct or union shall be alias-compatible, so it would be
perfectly legitimate to convert a pointer to a field-identifying type into
a pointer to the base type or another field-identifying type of the same
structure. Thus, one could use:
typedef struct Hey.value unsigned2x8;
and then use "unsigned2x8" as though it were an unsigned integer type.
TYPE ATTRIBUTES
I would suggest having an open-ended syntax for type-attributes; since there
is presently no context in which a type name may be followed by one or more
string literals, this could be accomplished reasonably concisely by saying
that string literals following a type name will be interpreted as attaching
attributes to the type. While the use of string literals would be unlike any
existing syntax convention, it would have the advantage that literals are
immune to the effects of the preprocessor; allowing the use of multiple
literals would make it possible to have macros expand into string literals
for use as type attributes.
For example, a declaration:
typedef struct Hey.value "align=free; kind=uwrap;" uwrap2x8;
would specify that uwrap2x8 may be aligned as the compiler sees fit (it's
likely that many compilers for machines with 8-bit bytes would choose to
16-bit-align it), and the type should behave like an unsigned integer type
which is larger than int (so operations with other things of the same kind
and size must yield results of the same size, rather than being promoted to
"int").
As another example, a declaration:
typedef char ("align=free;" FourBytes)[4];
would specify that even though individual members of the FourBytes array
would be packed one per byte, a compiler may select a coarser alignment
for type FourBytes itself (e.g. so a compiler which saw that code was
going to use multiple elements of the array could use one 32-bit load
to read them all and then use shifts to retrieve individual elements,
and code which was going to write to the first three elements could use
combine the first two into a 16-bit value, then store that as a half-word
and the third element as a byte.
SUMMARY
Given that compilers are already required to support bitfields, adding
support for programmer control of their layout shouldn't be a huge change.
Allowing bitfields to be assembled from multiple pieces might be a bit more
complicated to support, but existing code could be leveraged to support
that by using a compiler-internal bitfield type to assemble the value to be
stored, and then decomposing that into the pieces to be stored in other
individual bitfields.
Allowing structure fields to be used as types would seem like a bit more of
a change, but it would make it possible for code which expects integers to
be stored a certain way to be portable to machines that would normally
store them differently. It would also facilitate the writing of many kinds
of packet-handling code on platforms whose normal layouts don't match
those of the packets to be encoded or decoded.
Adding string-type attribute would make it possible to extend the language
in various useful ways, though that's best a subject for another post.
While it's unlikely that the Committee would adopt something so ambitious
even as a semi-optional feature which would require compilers to either
generate code according to spec **or indicate in some testable fashion [e.g.
by not pre-defining some macro] that the feature is not supported**, I
would think that with a suitable set of attributes such a feature could go
a long way toward making it easy to make a lot of code existing code
which relies upon memory layouts portable among compilers that support the feature.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-10-29 08:50 -0700 |
| Message-ID | <ln37wtbrmp.fsf@kst-u.example.com> |
| In reply to | #74136 |
supercat@casperkitty.com writes:
> On Wednesday, October 28, 2015 at 8:09:53 PM UTC-5, Keith Thompson wrote:
>> The length is unspecified to allow for the fact that implementations
>> may give array members different alignments according to their
>> lengths.
>
> Gaaaaaah!
The quoted text is a footnote that appeared in the C99 standard and was
later removed. If you're going to complain about it, I think that's
worth mentioning.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2015-10-29 12:25 -0700 |
| Message-ID | <160777ca-4b34-46f3-a2f3-027e67be2c8c@googlegroups.com> |
| In reply to | #74178 |
On Thursday, October 29, 2015 at 10:51:09 AM UTC-5, Keith Thompson wrote:
> The quoted text is a footnote that appeared in the C99 standard and was
> later removed. If you're going to complain about it, I think that's
> worth mentioning.
The text was changed to clarify that the offset of the field can't change in
response to the size of the container, but I think the implication is that
struct {
char x;
char b[3];
}
and
struct {
char x;
char b[4];
}
need not have the same field offset for b, since one of the b fields is of
type char[3] and the other is of type char[4]. A lot of the text in the
new version would only seem relevant if the placement and alignment of an
array field could vary depending upon the number of elements, which is a
freedom I'd never before heard of compilers using, and which will break
a lot of code that expects that structures will be laid out consistently.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-10-30 13:02 -0700 |
| Message-ID | <kfnwpu486q9.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #74128 |
Keith Thompson <kst-u@mib.org> writes:
> Tim Rentsch <txr@alumni.caltech.edu> writes:
>> Keith Thompson <kst-u@mib.org> writes:
>>
>>> supercat@casperkitty.com writes:
>>>> On Wednesday, October 28, 2015 at 2:15:29 PM UTC-5, Tim Rentsch wrote:
>>>>> supercat writes:
>>>>>> [snip]
>>>>
>>>> If I have one translation unit which contains the definitions:
>>>>
>>>> struct FOO { int x,y; char b[]; };
>>>> struct GOO { int x,y; char b[4]; };
>>>>
>>>> and another that contains those two definitions plus
>>>>
>>>> union FOOGOO { FOO f; GOO g; };
>
> [...]
>
>>> No. The standard explicitly says that the layouts must be identical in
>>> the second unit. It makes no such guarantee for the first.
>>
>> I believe it does, indirectly. The struct FOO in TU1 and the
>> struct FOO in TU2 are compatible types, so they must use the same
>> layout. Similarly for struct GOO's in TU1 and TU2. Therefore
>> the two struct types in TU1 must use the same layout, because the
>> corresponding compatible types might have to in another (unknown)
>> TU that has those corresponding types in a union.
>
> Right. I was thinking of the more general case of two distinct
> struct types with a common initial subsequence. I missed the fact
> that struct FOO and struct GOO are compatible.
Why do you say that struct FOO and struct GOO are compatible?
They don't look compatible to me, even inter-TU. Or did you (I
hesitate to even ask the question) mean something different from
compatible in the sense the Standard uses it?
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-10-30 13:35 -0700 |
| Message-ID | <ln611o9jt2.fsf@kst-u.example.com> |
| In reply to | #74361 |
Tim Rentsch <txr@alumni.caltech.edu> writes:
> Keith Thompson <kst-u@mib.org> writes:
>> Tim Rentsch <txr@alumni.caltech.edu> writes:
>>> Keith Thompson <kst-u@mib.org> writes:
>>>> supercat@casperkitty.com writes:
>>>>> On Wednesday, October 28, 2015 at 2:15:29 PM UTC-5, Tim Rentsch wrote:
>>>>>> supercat writes:
>>>>>>> [snip]
>>>>> If I have one translation unit which contains the definitions:
>>>>>
>>>>> struct FOO { int x,y; char b[]; };
>>>>> struct GOO { int x,y; char b[4]; };
>>>>>
>>>>> and another that contains those two definitions plus
>>>>>
>>>>> union FOOGOO { FOO f; GOO g; };
>>
>> [...]
>>
>>>> No. The standard explicitly says that the layouts must be identical in
>>>> the second unit. It makes no such guarantee for the first.
>>>
>>> I believe it does, indirectly. The struct FOO in TU1 and the
>>> struct FOO in TU2 are compatible types, so they must use the same
>>> layout. Similarly for struct GOO's in TU1 and TU2. Therefore
>>> the two struct types in TU1 must use the same layout, because the
>>> corresponding compatible types might have to in another (unknown)
>>> TU that has those corresponding types in a union.
>>
>> Right. I was thinking of the more general case of two distinct
>> struct types with a common initial subsequence. I missed the fact
>> that struct FOO and struct GOO are compatible.
>
> Why do you say that struct FOO and struct GOO are compatible?
> They don't look compatible to me, even inter-TU. Or did you (I
> hesitate to even ask the question) mean something different from
> compatible in the sense the Standard uses it?
I didn't entirely think it through, but ...
The x and y members have the same type, and the b members have
compatible types; see N1570 6.7.6.2 for compatibility of array types.
Normally two structure types are compatible only if they're the
same type, but 6.2.7p1 says that two structure types are compatible
if, among other requirements, their corresponding members have
compatible type *and* the structure types are declared in separate
translation units.
But structs FOO and GOO are declared in the same translation unit,
so as far as I can tell they're not compatible.
This isn't a part of the standard I've spent a lot of time thinking
about, so it's possible I'm missing something.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2015-10-30 19:57 -0700 |
| Message-ID | <kfnh9l79237.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #74365 |
Keith Thompson <kst-u@mib.org> writes:
> Tim Rentsch <txr@alumni.caltech.edu> writes:
>> Keith Thompson <kst-u@mib.org> writes:
>>> Tim Rentsch <txr@alumni.caltech.edu> writes:
>>>> Keith Thompson <kst-u@mib.org> writes:
>>>>> supercat@casperkitty.com writes:
>>>>>> On Wednesday, October 28, 2015 at 2:15:29 PM UTC-5, Tim Rentsch wrote:
>>>>>>> supercat writes:
>>>>>>>> [snip]
>>>>>>
>>>>>> If I have one translation unit which contains the definitions:
>>>>>>
>>>>>> struct FOO { int x,y; char b[]; };
>>>>>> struct GOO { int x,y; char b[4]; };
>>>>>>
>>>>>> and another that contains those two definitions plus
>>>>>>
>>>>>> union FOOGOO { FOO f; GOO g; };
>>>
>>> [...]
>>>
>>>>> No. The standard explicitly says that the layouts must be identical in
>>>>> the second unit. It makes no such guarantee for the first.
>>>>
>>>> I believe it does, indirectly. The struct FOO in TU1 and the
>>>> struct FOO in TU2 are compatible types, so they must use the same
>>>> layout. Similarly for struct GOO's in TU1 and TU2. Therefore
>>>> the two struct types in TU1 must use the same layout, because the
>>>> corresponding compatible types might have to in another (unknown)
>>>> TU that has those corresponding types in a union.
>>>
>>> Right. I was thinking of the more general case of two distinct
>>> struct types with a common initial subsequence. I missed the fact
>>> that struct FOO and struct GOO are compatible.
>>
>> Why do you say that struct FOO and struct GOO are compatible?
>> They don't look compatible to me, even inter-TU. Or did you (I
>> hesitate to even ask the question) mean something different from
>> compatible in the sense the Standard uses it?
>
> I didn't entirely think it through, but ...
>
> The x and y members have the same type, and the b members have
> compatible types; see N1570 6.7.6.2 for compatibility of array types.
>
> Normally two structure types are compatible only if they're the
> same type, but 6.2.7p1 says that two structure types are compatible
> if, among other requirements, their corresponding members have
> compatible type *and* the structure types are declared in separate
> translation units.
>
> But structs FOO and GOO are declared in the same translation unit,
> so as far as I can tell they're not compatible.
>
> This isn't a part of the standard I've spent a lot of time thinking
> about, so it's possible I'm missing something.
What I suspect you're missing is the sentence in 6.2.7 p1 that
says "If one is declared with a tag, the other shall be declared
with the same tag."
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-10-31 12:27 -0700 |
| Message-ID | <lnsi4q96um.fsf@kst-u.example.com> |
| In reply to | #74394 |
Tim Rentsch <txr@alumni.caltech.edu> writes:
> Keith Thompson <kst-u@mib.org> writes:
[...]
>> But structs FOO and GOO are declared in the same translation unit,
>> so as far as I can tell they're not compatible.
>>
>> This isn't a part of the standard I've spent a lot of time thinking
>> about, so it's possible I'm missing something.
>
> What I suspect you're missing is the sentence in 6.2.7 p1 that
> says "If one is declared with a tag, the other shall be declared
> with the same tag."
Yeah, that's what I missed. D'oh!
<waves hand> This conversation did not take place. I was never here.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Eric Sosman <esosman@comcast-dot-net.invalid> |
|---|---|
| Date | 2015-10-30 17:02 -0400 |
| Message-ID | <n10loj$56p$1@dont-email.me> |
| In reply to | #74128 |
On 10/28/2015 9:09 PM, Keith Thompson wrote:
> Tim Rentsch <txr@alumni.caltech.edu> writes:
>> Keith Thompson <kst-u@mib.org> writes:
>>
>>> supercat@casperkitty.com writes:
>>>> On Wednesday, October 28, 2015 at 2:15:29 PM UTC-5, Tim Rentsch wrote:
>>>>> supercat writes:
>>>>>> [snip]
>>>>
>>>> If I have one translation unit which contains the definitions:
>>>>
>>>> struct FOO { int x,y; char b[]; };
>>>> struct GOO { int x,y; char b[4]; };
>>>> [...]
>
> Right. I was thinking of the more general case of two distinct struct
> types with a common initial subsequence. I missed the fact that struct
> FOO and struct GOO are compatible.
$ cat foo.c
struct FOO { int x,y; char b[]; };
struct GOO { int x,y; char b[4]; };
int main(void) {
struct FOO f;
struct GOO g;
f = g;
return 0;
}
$ gcc -std=c99 foo.c
foo.c: In function ‘main’:
foo.c:6:6: error: incompatible types when assigning to type ‘struct
FOO’ from type ‘struct GOO’
f = g;
^
(Stupid gcc!)
--
esosman@comcast-dot-net.invalid
"Don't be afraid of work. Make work afraid of you." -- TLM
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.c
csiph-web