bart <bc@freeuk.com> writes:
One weak part of my C compiler is in initialisation:
* No runtime data is allowed for {...} data inside functions
In other words, {...} initializers can only contain constant
expressions. I presume this means you allow:
int a = 42;
int b = a;
but not
int a = 42;
int b = {a};
Out of curiousity, why are you using a C99 draft? Drafts of later
editions are available.
Paragraphs p17 to p38 described the rules for braces and for
designated initialisers. I vaguely remember that it was complicated, I
didn't realise it was this complicated!
For example, p35 shows this example:
struct { int a[3], b; } w[] = { [0].a = {1}, [1].a[0] = 2};
That seems clear enough to me.
It initializes w[0].a to {1}, w[1].a[0]
to 2, and everything else to the default value of 0.
None of it is necessary. It's convenient.
The convoluted examples are
a result of not imposing arbitrary restrictions. I believe the examples
in the standard are intended to clarify the rules and show what's
possible, not as examples of good coding style.
someexpr has to be a constant expression. You could as easily write:
int A[someexpr+1];
Designated initializers do not introduce any new danger.
It can get even more hairy:
int A[] = {[3]=1, [3]=2, [3]=A[3]};
What is the value of A[3]? My guess was 2, but when I tried it, it was
0 with gcc, 2 with tcc, an error with DMC, and -108754328 with clang.
Presumably this is because it's using an uninitialised value of A[3]?
But A[3] IS initialised, at least twice!
"The evaluations of the initialization list expressions are
indeterminately sequenced with respect to one another and thus the order
in which any side effects occur is unspecified."
One weak part of my C compiler is in initialisation:
* No support for designated initialisers
So I thought I'd bite the bullet and fix all this.
Until I came across section 6.7.9 (in my C99 draft standard,
which is Initialisation).
Paragraphs p17 to p38 described the rules for braces and for
designated initialisers. [...]
What this means is that you can declare a data strucure of any
complexity, and initialise random, nested elements within a
single set of { ... } braces. Here's a simple example:
int A[][10] = {
[7][3] = 99
};
I didn't know you could combine designators like this (I've
never seen such examples), but I don't believe that is
necessary. This example could also be written like this:
int A[][10] = {
[7] = {[3] = 99}
};
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
bart <bc@freeuk.com> writes:
One weak part of my C compiler is in initialisation:
[...]
* No support for designated initialisers
So I thought I'd bite the bullet and fix all this.
Good show!
Until I came across section 6.7.9 (in my C99 draft standard,
which is Initialisation).
Paragraphs p17 to p38 described the rules for braces and for
designated initialisers. [...]
What this means is that you can declare a data strucure of any
complexity, and initialise random, nested elements within a
single set of { ... } braces. Here's a simple example:
int A[][10] = {
[7][3] = 99
};
I didn't know you could combine designators like this (I've
never seen such examples), but I don't believe that is
necessary. This example could also be written like this:
int A[][10] = {
[7] = {[3] = 99}
};
It's true that the overall initializers in these two declarations
have the same result. Note however that the individual
initializer
[7][3] = 99
and the individual initializer
[7] = {[3] = 99}
have different meanings, and are different in at least two
significant ways, depending on the context in which they appear.
If you have difficulty seeing what those differences are, I'm
sure Keith Thompson can explain what's going on. Depending on
circumstances, either the first way or the second way could be
a better choice; it depends on what one wants to accomplish
and to convey.
Tim, you don't get to make an assertion and then pass off the work
of explaining it to someone else.
On 26/02/2024 17:04, Keith Thompson wrote:
bart <bc@freeuk.com> writes:
One weak part of my C compiler is in initialisation:
* No runtime data is allowed for {...} data inside functions
In other words, {...} initializers can only contain constant
expressions. I presume this means you allow:
    int a = 42;
    int b = a;
but not
    int a = 42;
    int b = {a};
Actually, that one works. This won't work:
   int c[2] = {a, b};
Out of curiousity, why are you using a C99 draft? Drafts of later
editions are available.
C99 is fine. There's little new in later versions that concerns my
compiler.
Paragraphs p17 to p38 described the rules for braces and for
designated initialisers. I vaguely remember that it was complicated, I
didn't realise it was this complicated!
For example, p35 shows this example:
    struct { int a[3], b; } w[] = { [0].a = {1}, [1].a[0] = 2};
That seems clear enough to me.
Well not to me, even after I'd got over my surprise at seeing 3
designators in a row.
 It initializes w[0].a to {1}, w[1].a[0]
to 2, and everything else to the default value of 0.
   w[0].a = {1, 0, 0};
   w[1].a = {2, 0, 0};
That is clearer. The only thing that elaborate initialiser adds, apart
from confusion, is info about the maximum dimension of w.
None of it is necessary. It's convenient.
You don't get my point. Designators were added for their convenience,
but that could have been done in a simple manner. It didn't need to complicate it by allowing arbitrarily long chains of designators:
   [3].a.b.c[4].d = x
VLAs could also have been done in a far simpler manner (by making the
VLA an attribute of a variable, not a type).
That would have sufficed for nearly all (perhaps even all) of the uses
I've ever seen of designated initialisers and VLAs.
The convoluted examples are
a result of not imposing arbitrary restrictions. I believe the examples
in the standard are intended to clarify the rules and show what's
possible, not as examples of good coding style.
But it means implementators have to ensure those examples work.
An example like this:
   int A[][10]= {
       [4][3]=10,
       [7][5]=20
   };
works in gcc and tcc. But older compilers like DMC and lccwin have
trouble with it (they work with single-designator examples).
someexpr has to be a constant expression. You could as easily write:
    int A[someexpr+1];
Designated initializers do not introduce any new danger.
It means a rogue value could be somewhere in the hundred designators of
A's init data rather than in one place.
It can get even more hairy:
    int A[] = {[3]=1, [3]=2, [3]=A[3]};
What is the value of A[3]? My guess was 2, but when I tried it, it was
0 with gcc, 2 with tcc, an error with DMC, and -108754328 with clang.
Presumably this is because it's using an uninitialised value of A[3]?
But A[3] IS initialised, at least twice!
"The evaluations of the initialization list expressions are
indeterminately sequenced with respect to one another and thus the order
in which any side effects occur is unspecified."
gcc with extra warnings said nothing about it. But this:
   int A[2] = {A[0], A[1]};
did produce a warning.
On 26/02/2024 13:29, bart wrote:
One weak part of my C compiler is in initialisation:
* No runtime data is allowed for {...} data inside functions
* For nested structs and arrays, the pattern of {...} braces must
exactly match the type structure, no missing braces allowed. (So {0}
can't be used to zero a struct; only missing elements in arrays are
allowed)
* No support for designated initialisers
* No support for compound literals (this is all part of the same aspect)
So I thought I'd bite the bullet and fix all this. Until I came across
section 6.7.9 (in my C99 draft standard, which is Initialisation).
Paragraphs p17 to p38 described the rules for braces and for
designated initialisers. I vaguely remember that it was complicated, I
didn't realise it was this complicated!
For example, p35 shows this example:
    struct { int a[3], b; } w[] = { [0].a = {1}, [1].a[0] = 2};
I couldn't grok it. I had to use this loop to find out what was going on:
  for (int i=0; i<sizeof(w)/sizeof(w[0]); ++i) {
      printf("(%d %d %d) %d\n", w[i].a[0], w[i].a[1], w[i].a[2],
w[i].b);
  }
What this means is that you can declare a data strucure of any
complexity, and initialise random, nested elements within a single set
of { ... } braces. Here's a simple example:
    int A[][10] = {
        [7][3] = 99
    };
I didn't know you could combine designators like this (I've never seen
such examples), but I don't believe that is necessary. This example
could also be written like this:
    int A[][10] = {
        [7] = {[3] = 99}
    };
This doesn't require combining designators, and here there is a better
match in the structure of the braces: '99' is inside 2 sets of braces
rather than 1. But there is a hidden danger here too:
    int A[] = {
       [someexpr] = 99
    }
For a local array, 'someexpr' could be 20, or it could be 2000000,
high enough to cause stack overflow if you're lucky. Or it could just
silently create a far bigger array than expected.
Here's another example from p36:
    int a[MAX] = {1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0};
p37 explains what it means, but not well. I think that the '6 4 2 0'
values follow element a[MAX-5]. It also suggests that is it possible
to define values multiple times:
    int A[] = {[3]=1, [3]=2, [3]=3};
It can get even more hairy:
    int A[] = {[3]=1, [3]=2, [3]=A[3]};
What is the value of A[3]? My guess was 2, but when I tried it, it was
0 with gcc, 2 with tcc, an error with DMC, and -108754328 with clang.
Presumably this is because it's using an uninitialised value of A[3]?
But A[3] IS initialised, at least twice!
This is all far more chaotic than I'd expected, and I decided not to
bother upgrading my compiler. I have trouble implementing something
that (1) I don't agree with because it's been poorly devised;(2) I
don't understand anyway.
I also want to understand better this.
I think the design must have some logic, I never stop to understand
this. I think the best way is see some generated code, because it is
maybe related with initialization performance. For instance, fill zeros
first then do the other initialization.
On 27/02/2024 12:57, Thiago Adams wrote:
On 26/02/2024 13:29, bart wrote:
One weak part of my C compiler is in initialisation:
* No runtime data is allowed for {...} data inside functions
* For nested structs and arrays, the pattern of {...} braces must
exactly match the type structure, no missing braces allowed. (So
{0} can't be used to zero a struct; only missing elements in
arrays are allowed)
* No support for designated initialisers
* No support for compound literals (this is all part of the same
aspect)
So I thought I'd bite the bullet and fix all this. Until I came
across section 6.7.9 (in my C99 draft standard, which is
Initialisation).
Paragraphs p17 to p38 described the rules for braces and for
designated initialisers. I vaguely remember that it was
complicated, I didn't realise it was this complicated!
For example, p35 shows this example:
struct { int a[3], b; } w[] = { [0].a = {1}, [1].a[0] = 2};
I couldn't grok it. I had to use this loop to find out what was
going on:
for (int i=0; i<sizeof(w)/sizeof(w[0]); ++i) {
printf("(%d %d %d) %d\n", w[i].a[0], w[i].a[1], w[i].a[2],
w[i].b);
}
What this means is that you can declare a data strucure of any
complexity, and initialise random, nested elements within a single
set of { ... } braces. Here's a simple example:
int A[][10] = {
[7][3] = 99
};
I didn't know you could combine designators like this (I've never
seen such examples), but I don't believe that is necessary. This
example could also be written like this:
int A[][10] = {
[7] = {[3] = 99}
};
This doesn't require combining designators, and here there is a
better match in the structure of the braces: '99' is inside 2 sets
of braces rather than 1. But there is a hidden danger here too:
int A[] = {
[someexpr] = 99
}
For a local array, 'someexpr' could be 20, or it could be 2000000,
high enough to cause stack overflow if you're lucky. Or it could
just silently create a far bigger array than expected.
Here's another example from p36:
int a[MAX] = {1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0};
p37 explains what it means, but not well. I think that the '6 4 2
0' values follow element a[MAX-5]. It also suggests that is it
possible to define values multiple times:
int A[] = {[3]=1, [3]=2, [3]=3};
It can get even more hairy:
int A[] = {[3]=1, [3]=2, [3]=A[3]};
What is the value of A[3]? My guess was 2, but when I tried it, it
was 0 with gcc, 2 with tcc, an error with DMC, and -108754328 with
clang.
Presumably this is because it's using an uninitialised value of
A[3]? But A[3] IS initialised, at least twice!
This is all far more chaotic than I'd expected, and I decided not
to bother upgrading my compiler. I have trouble implementing
something that (1) I don't agree with because it's been poorly
devised;(2) I don't understand anyway.
I also want to understand better this.
I think the design must have some logic, I never stop to understand
this. I think the best way is see some generated code, because it
is maybe related with initialization performance. For instance,
fill zeros first then do the other initialization.
The order of initialisation is by the order of the initialisers
(subject, as always, to the "as if" rule). So if you write :
int A[] = {[3]=1, [3]=2, [3]=3};
then the final "[3] = 3" is applied last, and overrides the others.
This does not seem very useful to me, as it stands. However, like
many features of C99, this has come about as a standardisation of gcc extensions, and that is how the gcc syntax worked - therefore, C99
copied it for consistency with existing code. In gcc, it makes much
more sense because you can write :
int A[] = { [0 .. 9] = 1, [10 .. 29] = 2, [5] = 17 };
and use this feature to override a value given in a range.
<https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html>
The order of evaluation of the initialisers, by contrast, is
unspecified. It is also unspecified if overridden initialisers are evaluated (for their side-effects) at all. Again, this is consistent
with the gcc extension, and it gives compilers more freedom in how
they organise the code. That does mean that code like this:
int A[] = {[3]=1, [3]=2, [3]=A[3]};
does not have a well-defined and consistent result. But then,
neither does "int x = x;", even though it is valid code. (It is
sometimes used as an idiom to tell static error checkers and other programmers that you know "x" is not initialised, and that there
should be no warning even if it appears to be read before
initialisation.)
Designated initialization of auto objects is small and unimportant convenience feature.
The big convenience gain comes when designated initializers applied
to static and global objects. And in that case most of the problems
mentioned above simply do not exist.
If I were in bart's place, authoring compiler that does not try to be
fully standard, I'd omit designated initialization of auto objects altogether.
One weak part of my C compiler is in initialisation:...
This is all far more chaotic than I'd expected, and I decided not to
bother upgrading my compiler. I have trouble implementing something that
(1) I don't agree with because it's been poorly devised;(2) I don't understand anyway.
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 546 |
Nodes: | 16 (0 / 16) |
Uptime: | 162:55:01 |
Calls: | 10,385 |
Calls today: | 2 |
Files: | 14,057 |
Messages: | 6,416,508 |