printf("%p\n", (void *)0);

J

James Kuyper

Antoine said:
En (e-mail address removed) va escriure:



Sorry, you left me confused:

Here are Dan's words again



And now I read it saying exactly the contrary of you...

I'm agreeing with "If it's not forbidden, it's allowed." I was actually
disagreeing with the conclusion that he reached from that principle. I
could have made that clearer; a smiley might have been appropriate.

In applying that principle, the important thing is what "it" refers to.
My "it" was "an implementation having a different alignment requirement
for an array than for it's element type". Dan's "it" was "code assuming
that an array has the same alignment requirements as its element type".
We reached exactly opposite conclusions from applying the same principle
in different ways.
I do not read it this way.
I really read that a complex has the same alignment contraint as an array of
two corresponding reals, as opposed for example to a structure of two
elements (which might be otherwise an alternative implementation I could
think about.) I do not see any mention or implication that a complex could
have a different alignment constraint than the corresponding real.

If it was intended that the standard mandate that a complex type have
the same alignment requirements as the corresponding real type, the
standard could have said so directly; the resulting wording would have
been much simpler than the wording that refers to an array type. The use
of the more complex wording implies that it might be imposing a
different constraint than the simpler wording would have.

However, that implication is fairly weak. The strongest argument for the
legality of different alignment requirements is the absence of an
explicit requirement that they be the same.
 
A

Antoine Leca

En news:[email protected], James Kuyper va escriure:
a smiley might have been appropriate.

OK, thanks for the clarification. Now I understand better.

If it was intended that the standard mandate that a complex type
have the same alignment requirements as the corresponding real
type, the standard could have said so directly;

As I wrote above, I do not read it saying such a thing /either/ (see also my
post re Dan about an inference of him jumping from arrays to the element
type.)

I just read that a complex have the same alignment contraints as an array of
the corresponding real.
(So the size of the former should be the double of that of the latter,
presumibly; or one can portably access the imaginary part through unions and
pointers. But these conclusions I am not 100% sure of; please correct me if
needed.)

However, that implication is fairly weak. The strongest argument
for the legality of different alignment requirements is the absence
of an explicit requirement that they be the same.

Another point here is the notorious existence of an architecture (Intel x86
segmented) where "big" arrays have different alignement constraints than
"small" ones. For the last weeks I am evaluating the possibility of malloc()
to deliver such "big" arrays in a conforming (hosted) implementation.

And until now I do not have a clear-cut answer.


Antoine
 
A

Antoine Leca

En (e-mail address removed) va escriure:
In fact, the standard implies (in 6.2.5p13) that an array of two
floating point objects might have different alignment requirements
than the element type of that array.

In a similar way, one could also read 6.5.6p7: "For the purpose of these
operators...", which could be inferred that outside such a purpose such
equivalence might not be true.

However I am not confident such reversed meaning reading are really worth
interpreting this way. I very much prefer active sentences.


Antoine
 
A

Antoine Leca

En news:[email protected], Wojtek Lerch va escriure:
But I fail to see how you can use this recursion argument to
"jump" to the element type: you only "proved" [...]

No, he has proved no such thing. The standard never promises

No what?

I intentionally used quotes above to show that I was not completely
satisfied by the demonstration. But whether the "lemma" is correct or not,
does not have anything to do with my point, which is that the main
demonstration is lacking one step.

OTOH, if you are indirectly challenging Dan here, I believe you already have
written it in (and the corresponding
subthread). We are on high-volume newsgroups, so I do not believe it is
valuable to duplicate posts.

Or did I miss something?


Antoine
 
J

James Kuyper

Antoine said:
En (e-mail address removed) va escriure:



In a similar way, one could also read 6.5.6p7: "For the purpose of these
operators...", which could be inferred that outside such a purpose such
equivalence might not be true.

I believe that is a correct reading of that clause. Can you think of any
case not covered by either 6.5.6p7 or 6.5.8p4 where a pointer to a
non-array object should be treated as equivalent to a pointer to the
first element of an array of length 1?
However I am not confident such reversed meaning reading are really worth
interpreting this way. I very much prefer active sentences.

I agree, but I consider the implication to be far clearer in 6.5.6p7
than in 6.2.5p13.
 
B

Brian Inglis

I don't believe it's possible for a short and an array of shorts to
have different required alignments. If short is 16 bits, a compiler
might choose to use, say, 32-bit alignment for arrays of shorts, but
it still has to be able to access an array of shorts that's 16-bit
aligned. For some purposes, a single short object is equivalent to an
array of 1 short.

ISTM that the Standard may be trying to avoid disallowing (say) vector
type processing of arrays where the VPU may have stricter array
alignment requirements than the CPU. In that case, it may be possible
that vector ops can't be performed on &a[1], although CPU ops work.
 
W

Wojtek Lerch

Antoine Leca said:
En news:[email protected], Wojtek Lerch va escriure:
But I fail to see how you can use this recursion argument to
"jump" to the element type: you only "proved" [...]

No, he has proved no such thing. The standard never promises

No what?

He has not proved, or even "proved", that all array types have the same
alignment constraint. If the conversion were guaranteed safe, his argument
would demonstrate that all arrays of two or more elements (of the same type)
must have the same alignment requirements. But his "proof" doesn't work if
you try to go from two-element arrays to one-element arrays.
I intentionally used quotes above to show that I was not completely
satisfied by the demonstration. But whether the "lemma" is correct or not,
does not have anything to do with my point, which is that the main
demonstration is lacking one step.

OTOH, if you are indirectly challenging Dan here, I believe you already
have
written it in (and the
corresponding
subthread). We are on high-volume newsgroups, so I do not believe it is
valuable to duplicate posts.

I apologize to readers of comp.lang.c for my unnecessary post, and any other
posts it has caused.
Or did I miss something?

Not much. Maybe the fact that the exact meaning of your quotation marks
around "proved" was not entirely obvious.
 
O

ozbear

printf("%p\n", (void *)0); /* UB, or not? Please explain your answer. */

I have followed this thread with great interest and read all the
messages, but now am unsure that I am any the wiser.

Did we reach any sort of consensus as to whether:
1) it is or is not UB

and in a subthread

2) whether or not arrays of some type have the same alignment
requirements as their element type?

Just asking...

Oz
 
T

Tim Rentsch

James Kuyper said:
For practical purposes, an implementation of malloc() could be written
that allocates from a fixed static array of memory. The array would have
a union type, where the union has members of many different types. The
wider the variety of types in the union, the more likely it is to have
universal alignment. In practice, if the union contains short, int,
long, long long, float, double, long double, void*, and void(*)(void),
it's got a pretty good chance of having universal alignment. However,
there's no finite set of types which is guaranteed by the standard to
give such a union universal alignment.

This thread has been interesting. Let's see if we can sort things out
a bit.

First, James Kuyper's statement that there is no finite set of types
which is guaranteed to give universal alignment is correct. That's
implied by array alignment not having to be the same for arrays of
differing lengths (see next), but even without considering arrays the
statement is correct, because of structs. Suppose sizeof(short) == 2
and alignment_of(short) == 1 (I trust everyone follows my meaning
here). Then the types

struct { short s; }
struct { struct { short s; } }
struct { struct { struct { short s; } } }
...

could have alignment of 1 up to, say, four nestings of struct, but for
five or greater nestings could have alignment of 2. I grant that an
implementation would be particularly perverse to do such a thing, but
it seems to be allowed. For a more pedestrian example, the types

struct { short a; }
struct { short a; short b; }
struct { short a; short b; short c; }
...

could have an unbounded number of different alignment requirements.
Right?

Getting back to arrays of differing numbers of elements, it seems
clear that the standard intends that arrays of different lengths can
have different alignment requirements, at least in some circumstances.
Looking at 6.7.2.1 p16, p17 and note 106,

16 As a special case, the last element of a structure with more
than one named memeber 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.106) 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.

17 EXAMPLE Assuming that all array members are aligned the same,
after the declarations

struct s { int n; double d[]; };
struct ss { int n; double d[1]; };

the three expressions

sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)

have the same value. The structure struct s has a flexible
array member d.

------------------------

106) The length is unspecified to allow for the fact that
implementations may give array members different
alignments according to their lengths.

Notice the second to last sentence in paragraph 16, especially the
part after the semicolon. Clearly the standard anticipates having
different alignment requirements for arrays of differing lengths, at
least when they are structure members; it isn't a big stretch to
conclude that they are allowed to have different alignment
requirements outside of structures also.

If an array of unspecified length can have a different alignment
requirement than an array of 1 element, then the alignment
requirement for the element type need not match the alignment
requirement for arrays of that type. For 'short', for example,
it is always true that

alignment_of(short) <= alignment_of(short[])
alignment_of(short) <= alignment_of(short[N])

where 'N' is the (compile-time constant) array dimension. (Another
way of expressing the first line is "the alignment of short is no more
restrictive than the alignment of short[].") The reason for this is
obvious - any array of short can easily be used to make an expression
that yields a short (lvalue), which much be properly aligned. Also,
it is always true that

alignment_of(short[]) <= alignment_of(short[N])

for any N, because a declaration 'extern short whatever[];' matches
array definitions of any length. So, if it can be true that

alignment_of(short[]) < alignment_of(short[1])

as is implied by the phrasing used in p17, then the alignment
requirements for short may differ from the alignment requirements for
an array of short (and similarly other types). This doesn't matter
too much in the context of the original discussion, since when we are
concerned only with more restrictive alignment requirements the
array type T[1] can be used in place of plain T with no loss of
alignment requirements.

As a practical matter, we would expect that alignment requirements
will match up for unspecified length arrays and length 1 arrays. That
is, we expect

alignment_of(T[]) == alignment_of(T[1])

reason being, consider the following code:

short short_array_TWO[2];
short (*pointer_to_short_array_TWO)[2];
short (*pointer_to_short_array)[];
short (*pointer_to_short_array_ONE)[1];

pointer_to_short_array_TWO = &short_array_TWO;
pointer_to_short_array = pointer_to_short_array_TWO;
pointer_to_short_array_ONE = pointer_to_short_array;

/* Most would expect the last assignment */
/* to be "safe" as well as legal. */

Or, to say this another way, we expect always to be able to use a
pointer to some array (that is, some actual array object) as a pointer
to an array of length 1. An implementation that didn't do this might
be useful as some sort of debugging or diagnostic aid, but it's highly
unlikely this choice is one an implementation intended for production
would make.

Even though the standard says very little about what the alignments of
various types are allowed to be, we can put some limits what they
can be. In particular,

sizeof(T) % alignment_of(T) == 0

must hold for any type T (perhaps of sufficiently small size), because
we can make arrays:

T t[2];

If sizeof(T) is 4, the alignment_of(T) must be 1, 2, or 4; it can't
be 3, 5, or 8, because then the alignment of t[1] would be wrong. So,
if sizeof(short) == 2, and if we accept sizeof(T[N]) == (N)*sizeof(T)
then the alignment of

short t2[2];

might be 4, but that alignment can't apply to short[3]; otherwise the
case of

short t_2_3[2][3];

would give incorrect alignment for the short[3] array at t_2_3[1].

It's hard to draw any absolute conclusions about what must be true
when converting between arrays of differing lengths. Any change of
length in array type means a conversion (normally of a pointer to
array), and the standard doesn't guarantee much about what pointer
conversions do. The relevant section (6.3.2.3 p7) also says that "if
the resulting pointer is not correctly aligned for the pointed-to
type, the behavior is undefined."

In practical terms it's unlikely that alignment requirements for
differing length arrays will cause a problem in most normal code. The
reason is, most code turns any array uses immediately into pointers,
and going to an element pointer is safe (as long as the array has
alignment suitable for some length array, which is to say, for T[]).

Using pointer-to-array types might seem more likely to cause problems
in the "universal union allocator" scenario. However, that's not as
likely as it seems, because code that seems perfectly reasonable (and
has nothing to do with malloc or storage allocation) would also cause
problems. Some examples:

short t_2_3[2][3];
short (*psa)[];
short (*psa2)[2];

psa = &t_2_3[1];
psa2 = psa;

and

short short_2049[2049];
short (*w)[];
short (*x)[][1];
short (*y)[];
short (*z)[2048];

w = & short_2049;
x = (short (*)[][1]) w; /* cast needed */
y = & (*x)[1];
z = y;

There is one cast here, the assignment of 'x' from 'w'. The cast is
required to convert the variable 'w', a pointer-to-array-of-shorts, to
the variable 'x', a pointer-to-array-of-array[1]-of-shorts; in
effect, each short in the original array is being converted into a
short[1] array. This conversion corresponds to the expectation

alignment_of(T[]) == alignment_of(T[1])

explained earlier. (And that pointer conversion works as we expect,
as mentioned by Wojtek Lerch.) All the other assignments are
conversions between compatible types.

In each example, the last statement may evoke undefined behavior
because the (implicit) conversion may produce a pointer of unsuitable
alignment. I certainly wouldn't deny that the standard allows that to
happen. It seems unlikely that it will though, at least directly;
consider 6.3 p2

Conversion of an operand value to a compatible type causes no
change to the value or to the representation.

So an implementation would pretty much need to go out of its way to
cause problems for these examples to cause problems directly. As long
as all that's done with these array pointers is turn them into regular
pointers, most likely all will be well.

Where there are likely to be problems is arrays or array types
(perhaps contained in structs) that are unknown to "your" code but
used in implementation files for some of the necessary library
functions or whatever. For example, the 'jmp_buf' type, stated as
being an array type, might require an alignment that must match the
alignment of a cache line. (So don't forget to include a jmp_buf
member in the "universal union" type. :)

Summing up: technically, doing almost any kind of pointer conversion
_not_ guaranteed to have no alignment problems might _have_ alignment
problems, and evoke undefined behavior; arrays of differing lengths
can have different alignment requirements, and converting between
pointers to arrays of differing lengths, even between compatible
types, it's still a conversion that can result in alignment mismatch
and so evoke undefined behavior.

In practical terms, however, it's safe for "regular" types to convert
between pointers to array types of differing lengths. If a conversion
to a pointer-to-array type is necessary, normally prefer use of a
pointer to array of unspecified length ('T(*)[]') type to one with an
explicit length.
 
J

James Kuyper

Tim said:
This thread has been interesting. Let's see if we can sort things out
a bit.

First, James Kuyper's statement that there is no finite set of types
which is guaranteed to give universal alignment is correct.

There's a simpler way to reach that conclusion. There's no limit to the
number and alignment requirements of extended integer types, and
extended integer types will in general have names that can't be used in
portable code. Some, many, perhaps even all of the extended types will
be referred to by one or more standard-named typedefs. However, as long
as an implementation has even a single extended integer type for which
none of the standard-defined types is a typedef, portable code has no
way to refer to that type. That type can have an alignment restriction
that is stricter than the restriction on any standard-named type.
 
K

Keith Thompson

James Kuyper said:
There's a simpler way to reach that conclusion. There's no limit to
the number and alignment requirements of extended integer types, and
extended integer types will in general have names that can't be used
in portable code. Some, many, perhaps even all of the extended types
will be referred to by one or more standard-named typedefs. However,
as long as an implementation has even a single extended integer type
for which none of the standard-defined types is a typedef, portable
code has no way to refer to that type. That type can have an alignment
restriction that is stricter than the restriction on any
standard-named type.

Realistically, it's unlikely that any integer type will have stricter
alignment requirements than intmax_t and/or uintmax_t, but of course
there's no guarantee of this.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,754
Messages
2,569,527
Members
44,998
Latest member
MarissaEub

Latest Threads

Top