Keith Thompson said:
For example, a pointer object might require 4-byte alignment, whereas
a char object requires only 1-byte alignment. When the standard says
void* and char* have to have the same alignment requirements, the
intent is that they're interchangeable as arguments to functions.
Right.
extern char *pc;
void free(void *p);
free(pc); //correct
(Note: we don't need same representation requirement in the above
example. This requirement is necessary eg. for variadic arguments:
printf("%p", pc);
- no cast to void* required.)
Requiring 1-byte alignment for char* but only 4-byte alignment for
void* could break this.
No. I see no reason to require same alignment for types void* and char*,
because in function arguments they're passed by *value* (think conversion).
Similarly, long and char do not have to have same alignment, but we may
always use them in arguments "interchangeably":
extern long l;
extern char c;
int f_char(unsigned char c);
int f_long(unsigned long l);
f_char(l); //both calls correct
f_long(c);
Look at the other references to alignment requirements in 6.2.5; they
clearly refer to the alignment of the type itself. 6.2.5#26 uses
similar wording, so it refers to the alignment of the pointers
themselves. (Alignment requirements for character types are discussed
elsewhere.)
I have looked in the entire text. The Standard uses the word "alignment"
in three ways: type (or object) alignment; pointer value alignment
(it uses words: "value", "result", "address"); or pointer alignment
(the way we discuss now).
The last manner is also found in: 6.3.2.3#7 (Pointers) and 7.20.3#1
(Memory management functions). See for yourself: in both cases "pointer
alignment" refers to the pointer *value*, not pointer type.
Actually, this is informally speaking, not strictly speaking.
Informally, some talk about aligning a pointer when we really mean
aligning the *VALUE* assigned to the pointer which is equivalent to
aligning the object the pointer points to.
Strictly speaking, when we talk about aligning a pointer we mean
aligning the pointer itself.
So this is actually what you and Keith think the Standard refers to!
So the fight is over "strictly" and "informally". I think that
the Standard in all cases means "informally".
Just as note, this is what allows a struct type to contain a member
which is a pointer to the same struct type (think linked list). At
the time the member is being processed by the compiler, the alignment
requirements of the struct are not yet known (the struct is still
incomplete) but the alignment requirements of the pointer are known so
the compiler can decide how much padding is needed.
While this might be a problem, I agree, this is an internal matter
of the implementation how it solves this (pointer type alignment and
padding before it). The Standard is a contract between an implementor
and a programmer. There is nothing a programmer can gain from
knowing that all pointer-to-struct types have same alignment and
thanks to it the compiler can easily construct a structure type.
I just think your argument misses the point.
OT:
The compiler might construct a struct type in several passes, each
time adjusting the padding before the pointer member.
This process must end, because there exists a maximum alignment.
========
My arguments:
6.2.5#26 (extended quote, to get more context)
A pointer to void shall have the same representation and alignment
requirements as a pointer to a character type.39) Similarly,
pointers to qualified or unqualified versions of compatible types
shall have the same representation and alignment requirements. All
pointers to structure types shall have the same representation and
alignment requirements as each other. All pointers to union types
shall have the same representation and alignment requirements
as each other. Pointers to other types need not have the same
representation or alignment requirements.
39) The same representation and alignment requirements are meant
to imply interchangeability as arguments to functions, return
values from functions, and members of unions.
Rationale:
[...] A pointer to void must have the same representation
and alignment as a pointer to char; the intent of this rule is
to allow existing programs that call library functions such as
memcpy and free to continue to work.
Remark: It is obvious the Standard means "pointers to (structure types)",
not "(pointers to structure) types". This follows from English grammar
rules, but please don't ask me for chapter and verse!
The first part the Std talks about the "compatibility" of void* and char*
types. Here it definitely refers to alignment of pointer *values*.
This is required for conversions between void* and char* to be well
defined. Of course, the Std cannot say directly about requirements
for the types they point to, because void is not a complete type (but
this is the intention if we forget it for a while and treat void as an
integer type with size one, but not compatible with char).
(OTOH the Std could formally define alignment of void type to be equal
to that of char type.)
The Standard cannot refer to the alignment of _types_ void* and char*.
If it did, it would only concern conversions between void** and char**.
The only standard functions with that type are strto?() family and friends
(and then we should add same requirement for wchar_t* to be consistent).
The rationale mentions only functions like free() or memcpy(), ie. ones
that probably historically had int free(char*) prototypes before the
type void was introduced. Here, the alignment compatibility between
the _values_ of type void* and char* is essential - IOW it means the
resolution of void* and char* pointer types is the highest and both
are equivalent in this matter.
Then, after the word "Similarly", the Standard talks about alignment
of pointers to compatible types, struct and unions, and other types in
the same manner. There is no reason to believe it uses similar wording
in a different manner this time.
There is no advantage in guaranteeing same alignment for "pointers
to incompatible types" types (at least I can't think of any).
All of them can be stored as void* if need be.