Discussion in 'C Programming' started by anish singh, Mar 7, 2014.

1. ### Stephen SprunkGuest

That's an array of two doubles. He was using "pair of doubles" to mean
a type guaranteed to have 16-byte alignment, unlike an array of doubles.
Apparently not:

% cat foo.c
#include <stdio.h>
#define alignof(t) __alignof__(t)
struct twodouble { double x; double y; };
int main(void) {
printf("alignof(double) = %zu\n", alignof(double));
printf("alignof(double[2]) = %zu\n", alignof(double[2]));
printf("alignof(double complex) = %zu\n", alignof(double _Complex));
printf("alignof(struct twodouble) = %zu\n", alignof(struct twdouble));
}
% gcc -std=c99 -pedantic -W -Wall foo.c
% ./a.out
alignof(double) = 8
alignof(double[2]) = 8
alignof(double complex) = 8
alignof(struct twodouble) = 4
% gcc -v
Using built-in specs.
Target: i486-linux-gnu
....

The first three are what I expected, but the last one has me quite
confused; how can a struct have less strict alignment than its members?
Of course, but without such a guarantee, how useful is that?

S

Stephen Sprunk, Mar 29, 2014

2. ### Keith ThompsonGuest

N1570 6.5.3.4p3:

The _Alignof operator yields the alignment requirement of its
operand type. The operand is not evaluated and the result is
an integer constant. When applied to an array type, the result
is the alignment requirement of the element type.

So _Alignof(double[2]) == _Alignof(double), by definition.

[...]
It's not free to *require* a stricter alignment. Given:

double arr[3];

both arr[0] and arr[1] can be treated as the first element of a
double[2] object. (I'm *fairly* sure of that, but I don't have a
citation to prove it.)

Of course a compiler can allocate arrays at stricter alignments than
required, but it must still be able to (generate code to) access them at
the alignment of their element type.

Keith Thompson, Mar 29, 2014

3. ### Keith ThompsonGuest

N1570 6.5.3.4p3:

The _Alignof operator yields the alignment requirement of its
operand type. The operand is not evaluated and the result is
an integer constant. When applied to an array type, the result
is the alignment requirement of the element type.

It might have made more sense for a complex type's alignment to be the
same as the alignment of a struct containing two members of the real
type -- but then that would in principle have permitted padding between
the members and/or after the second one (though no sane compiler is

Keith Thompson, Mar 29, 2014
4. ### James KuyperGuest

I thought he meant "pair of doubles" to refer to precisely that: two
doubles. What he was saying was that there was a significant advantage,
on such platforms, to aligning such pairs on 16-byte boundaries; not
that such alignment was a part of the definition of the phrase "pair of
doubles".
Well, it is only something that is permitted; it isn't mandatory.
Perhaps the implementors didn't consider the increased speed of the
instructions he was talking about to be a sufficiently important issue.
It seems to me that this cannot be correct. It is permitted for a struct
to have stricter alignment than it's member's types, but not for it to
be less strict.
It allows the compiler to align arrays of doubles that have an even
length more strictly than individual double objects, therefore
increasing the opportunities to make the code more efficient by taking
advantage of the special instructions that can only be used on suitably
aligned pairs of doubles. That strikes me as useful.

James Kuyper, Mar 29, 2014
5. ### James KuyperGuest

Ah - that's new, I think - I haven't had time to fully digest the
changes made in C2011. In principle the same requirement could have been
worded in earlier versions of the standard in terms of the alignment of
the type without mentioning _Alignof(), but I don't think that it was.
Am I correct?

James Kuyper, Mar 29, 2014
6. ### Eric SosmanGuest

^ ?

Eric Sosman, Mar 29, 2014
7. ### James KuyperGuest

That seems like an over-reaction. Actual C compilers are far less
user-unfriendly than the C standard allows them to be. Also, C2011 added
_Alignas(), which would seem to address the issue you're raising.

James Kuyper, Mar 29, 2014
8. ### Stephen SprunkGuest

We know that _Alignof(double) == 8 on the platform in question, so if he
is using "pair of doubles" to refer to something with an alignment of
16, he cannot have meant simply two doubles or an array of two doubles,
so I took my best guess at what he meant: some new type that has a size
of 16+.
It's not even permitted. As Keith cited, according to N1570 6.5.3.4p3,
_Alignof(double[2]) == _Alignof(double).
There's nothing stopping the implementation from _delivering_ stricter
alignment than promised in order to make use of such instructions, but
there doesn't seem to be any way for it to promise to do so.
So, compiler bug?

It seems obvious that, for array indexing to work, a struct's alignment
must be at least as strict as any of its members, but I can't find the
specific text in N1570 that says so.

S

Stephen Sprunk, Mar 29, 2014
9. ### Stephen SprunkGuest

Changing the alignment could break binary compatibility, which is a
bigger issue on some platforms than others.

My GCC targets "i486", and it offers 8-byte alignment for double. There
is little harm in requiring 8-byte alignment when its output runs on a
486, but there is significant benefit when it runs on a Pentium or later.
True, but we know that unaligned accesses have a performance penalty, so
implementations _should_ align when possible as a QoI matter. And C has
to work on systems that don't support unaligned access at all.
*shrug* That's obviously OT here.
An implementation _can_ fairly reliably generate them as a QoI matter,
even if it isn't required to do so. But since it's not required, it
still has to be able to handle the unaligned case, which isn't
completely avoidable.
If it doesn't contain only doubles? Then we'd need to know what type
the other members are and what their alignment is to be able to
determine the alignment for the whole structure.

S

Stephen Sprunk, Mar 29, 2014
10. ### Eric SosmanGuest

No, typo. Go upthread and look carefully at the code.

Eric Sosman, Mar 29, 2014
11. ### glen herrmannsfeldtGuest

(snip, someone wrote)
Since x86 (I presume that is what this is) doesn't require any
alignment, it isn't so obvious that it is wrong. It does seem
unusual, though.

-- glen

glen herrmannsfeldt, Mar 29, 2014
12. ### Keith ThompsonGuest

Search for every occurrence of "alignment" in N1256, I see no explicit
statement about the alignment of array types (which is a bit
surprising).

But I think the *required* alignment for an array type was already the
same as the required alignment for the element type.

For example, I *think* this program, which treats "slices" of a
double[3] array as double[2] arrays, is strictly conforming in
both C99 and C11. If double[2] could have a stricter alignment
requirement than double, its behavior would be undefined under any
implementation that imposes such an alignment.

#include <stdio.h>

typedef double pair[2];

static void print(pair *p) {
printf("%g %g\n", (*p)[0], (*p)[1]);
}

int main(void) {
double arr[3] = { 10.0, 20.0, 30.0 };
pair *p0 = (pair*)&arr[0];
pair *p1 = (pair*)&arr[1];
print(p0);
print(p1);
}

Keith Thompson, Mar 29, 2014
13. ### Tim RentschGuest

Just to be an asshole.. oh sorry, to play devil's advocate..
consider a 64-bit processor with cache lines of 4 64-bit
units, which is to say 4x8 = 32 bytes. On such a machine,
it might make sense to have structs aligned on 32-byte
boundaries, for performance reasons. Under these conditions
an implementation would be obliged to provide trailing
padding, so all structs would be a multiple of 32 bytes
in length. Not very likely perhaps, but not insane
either.

Tim Rentsch, Mar 29, 2014
14. ### Tim RentschGuest

Strictly speaking, no, but a more interesting question is since
_Alignof was mentioned specifically why not look it up in a more
up-to-date version of the Standard before responding, especially
knowing that one's knowledge of the 2011 standard (where _Alignof
is defined) is not really up to par yet?

Tim Rentsch, Mar 29, 2014
15. ### glen herrmannsfeldtGuest

(snip, someone wrote)
Well, there are systems that require alignment (or copying
somewhere else and copying the result back) and ones where it
is just somewhat faster.

-- glen

glen herrmannsfeldt, Mar 30, 2014
16. ### Tim RentschGuest

If you mean the example program as part of an argument, it's
a circular argument. The program is strictly conforming if
and only if the alignment of double[2] must be the same as
the alignment of double. Saying the given program is strictly
conforming is just a sneaky way of begging the question.

I partly agree with your comment re alignment in C99/N1256, in the
sense that apparently there is an /expectation/ that alignment of
arrays must match the alignment of their elements. However, I
don't find any statement, or combination of statements, in
C99/N1256 that either requires or logically implies that such a
limitation must hold. This omission means some code that looks
reasonable might have undefined behavior. Consider for example
the following code fragment, accepted under C99 (and also C90):

int a23[2][3];
int a32[3][2];
int (*a)[];
int (*b)[2] = a32;
int (*c)[3] = a23;
a = a23;
b = a;
a = a32;
c = a;

Does this code have undefined behavior or not? In particular, are
the assignments to b and c okay, which wouldn't be allowed without
using 'a' as an intermediary? AFAICS the Standard doesn't require
the alignments of the types (int[2]) and (int[3]) to be the same.
(To simplify the discussion let's assume the alignment of (int[])
is the same as that of (int) - this doesn't change the question in
any significant way.) If indeed it is the case that the Standard
does not either require or logically imply that the alignments of
these two types must be the same, then an implementation is free
to make them different, which means the program has undefined
behavior. Furthermore such a possibility isn't that farfetched.
If we add a few lines

b[2][1] = 0;
c[1][2] = 0;

we still have acceptable standard C code (ie, does not require a
diagnostic), yet it certainly crosses into undefined behavior.
If we take the Standard at its word, then the alignments of the
types involved here are not constrained to have the same value,
and when they are different the semantics of pointer conversion
explicitly deems such conversions undefined behavior.

Let me say again that I think the Standard was written with the
expectation, and also is read by most people as meaning to imply,
that the alignment of array types will be the same as that of
their elements. However I don't find any text, either normative
or informative, in the Standard itself (ie, pre-C11) that supports
this supposition.

Tim Rentsch, Mar 30, 2014
17. ### James KuyperGuest

On 03/29/2014 02:02 PM, Stephen Sprunk wrote:
....
I've acknowledged that I missed that clause. The restriction it imposes
is new in C2011, even thought it could have been expressed in different
terms in previous versions of the standard.
Alignment requirements are implementation-defined, which means that a
conforming implementation of C must come with documentation that
describes them. Prior to C2011, that documentation could have specified
that double[N] will have 16 byte alignment whenever N is even. Keith
postulates to the contrary, but I don't think it can easily be proven.
It's been pointed out that the code provided says alignof(struct
twdouble). Since no struct with that name has been defined, it's an
incomplete type. I'm not sure what the specification of GNU C's
__alignof__() is, but I'm surprised that giving it an incomplete type
didn't result in an error message. I doubt that there's anything that
can be usefully guaranteed about it's result.
There is no text that says so directly. It's something that can be
derived from what the standard says about arrays, the offsetof() macro
(which implies that the offset is a constant depending only upon the
struct definition, rather than being dependent upon which instance of
the struct is being referred to), and alignment requirements.

James Kuyper, Mar 31, 2014
18. ### Stephen SprunkGuest

They matched in what I compiled/ran; I changed the struct's name before
posting for clarity but made a typo.

S

Stephen Sprunk, Mar 31, 2014
19. ### Eric SosmanGuest

Would it be too much trouble to ask to see the exact source
you actually compiled and run? The source as posted has at least
two problems: The twodouble/twdouble mismatch, and the use of
__alignof__ (compiler-specific magic? another typo? or what?).

Eric Sosman, Mar 31, 2014
20. ### Keith ThompsonGuest

It wasn't *quite* meant as an argument, more as a way to restate
the question in a perhaps clearer manner.

I *think* that C99 permits the kinds of access in this program (and
I'd be very surprised to see an implementation where it doesn't work)
but I haven't found wording that proves or disproves it.

[...]

Keith Thompson, Mar 31, 2014