Thread View: comp.lang.c.moderated
11 messages
11 total messages
Started by USENET News System
Wed, 03 Apr 1996 13:05
Newsgroups: comp.lang.c,comp.lang.c++,comp.lang.c++.moderated,comp.lang.c.moderated
Author: USENET News System
Date: Wed, 03 Apr 1996 13:05
Date: Wed, 03 Apr 1996 13:05
33 lines
1446 bytes
1446 bytes
/* Under the Microsoft Visual C++ 2.0 and 4.0 the following
was observed. */
typedef struct { int a; int b; } *TA;
typedef struct { int a; int b; } *TB;
void
f( TA x, TB y )
{
f ( y, x); /* This compiles fine */
}
typedef struct tagA { int a; int b; } *UA;
typedef struct tagB { float ttt; } *UB;
void
g( UA x, UB y )
{
g ( y, x); /* This does not */
/* error C2664: 'g' :
cannot convert parameter 1 from 'struct tagB*' to 'struct tagA*' */
}
/* My question is this: Why does the function f compile without errors?
I've tried this code in gcc and I get an error in both functions.
Who is right here? I suspect gcc is correct, but is Microsoft's
implemention allowable in the standard also? */
# Eldon Olmstead, Software Engineer
# PRIOR Data Sciences Ltd, Phone: (902) 423-1331, Ext 240
# 5475 Spring Garden Road, Suite 601,
# Halifax, Nova Scotia, B3J-3T2
# Canada
Compatible types (was: Re: <a list of news groups>)
Author: Jos A. Horsmeier
Date: Fri, 05 Apr 1996 00:30
Date: Fri, 05 Apr 1996 00:30
101 lines
4150 bytes
4150 bytes
In article <4jtt2c$3eq@solutions.solon.com>, news@spar.ca wrote:
|/* Under the Microsoft Visual C++ 2.0 and 4.0 the following
| was observed. */
|
|typedef struct { int a; int b; } *TA;
|typedef struct { int a; int b; } *TB;
|
|void
|f( TA x, TB y )
|{
| f ( y, x); /* This compiles fine */
|}
|
|typedef struct tagA { int a; int b; } *UA;
|typedef struct tagB { float ttt; } *UB;
|
|void
|g( UA x, UB y )
|{
| g ( y, x); /* This does not */
| /* error C2664: 'g' :
| cannot convert parameter 1 from 'struct tagB*' to 'struct tagA*' */
|}
|
|/* My question is this: Why does the function f compile without errors?
| I've tried this code in gcc and I get an error in both functions.
| Who is right here? I suspect gcc is correct, but is Microsoft's
| implemention allowable in the standard also? */
I might be totally wrong, but I suspect Microsoft's compiler to be right
here. First the easy part:
In your second example 'struct { int a; int b; }' and 'struct { float ttt; }'
or not compatible types. This is what the Standard has to say about it:
6.1.2.6 Compatible type and composite type
'Two types have compatible type if their types are the same. [ ... ]
Moreover, two structure [ ... ] types declared in separate translation
units are compatible if they have the same number of members, the same
member names, and compatible member types; for two structures, the
members shall be in the same order; [ ... ]'
6.5.4.1 Pointer declarators
'For two pointer types to be compatible, both shall be identically
qualified and both shall be pointers to compatible types'.
[ end of quotes ]
Obviously the two declarators in your second example do not denote
compatible types, hence the whining of both compilers.
Here's the difficult part; in your first example both structure declarators
_do_ denote compatible types (the names, types and order of elements of
both structures are identical). Here's the Standard again telling
about the do's and don'ts of actual function parameters:
6.3.2.2 Function calls
' Each argument shall have a type such that its value may be
assigned to an object with the unqualified version of the type
of its corresponding parameter. [ ... ] An argument may be an expression
of any object type. In preparing for the call to a function, the aruments
are evaluated, and each parameter is assinged the value of the
corresponding argument.'
[ end of quote ]
So far so good; Maybe the assignment of (actual) parameter values
to the parameters themselves makes gcc whine. Back to the standard:
6.3.16.1 Simple assignment
'both operands are pointers to qualified or unqualified versions of
compatible types [ ... ]'
[ end of quote ]
As far as I can see, there's no (obvious) reason for the gcc compiler
to complain about your first example. If I put on my lawyer's hat I
could argue about the phrase 'separate translation units' in paragraph
6.1.2.6, but I don't even have a lawyer's hat, so I won't ;-)
A compiler is entitled to whine about perfectly conforming translation
units, but I don't find that a valid reason either.
Correct me if I'm wrong (takers, anyone?) but, IMHO gcc has nothing to
complain about in the first example.
kind regards,
Jos aka jos@and.nl
ps. My xlc compiler on one of our SP/2 beasts even complains about this
in the following silly manner: ' Function argument assignment between
types "struct {...}*" and "struct {...}*" is not allowed.'
Those two struct thingies look quite compatible to me ... ;-)
--
Atnwgqkrl gy zit vgksr, ug qshiqwtzoeqs!
Re: Newsgroups: comp.lang.c,comp.lang.c++,comp.lang.c++.moderated,comp.lang.c.moderated
Author: rdwells
Date: Fri, 05 Apr 1996 00:38
Date: Fri, 05 Apr 1996 00:38
28 lines
1426 bytes
1426 bytes
USENET News System wrote:
>
> /* Under the Microsoft Visual C++ 2.0 and 4.0 the following
> was observed. */
>
> typedef struct { int a; int b; } *TA;
> typedef struct { int a; int b; } *TB;
>
> void
> f( TA x, TB y )
> {
> f ( y, x); /* This compiles fine */
> }
I think VC++ has this right. Section 6.1.2.6 of the standard says:
Moreover, two structure, union, or enumeration types declared
in separate translation units are compatible if they have the
same number of members, the same member names, and compatible
member types; for two structures, the members shall be in the
same order...
Of course, this mentions "separate translation units", which
doesn't apply in this case, so perhaps my reading is incorrect.
I'm assuming here that you're treating this as a C source file,
and not a C++ file. I'll let someone else determine whether
this is valid C++ or not.
Re: Newsgroups: comp.lang.c,comp.lang.c++,comp.lang.c++.moderated,comp.lang.c.moderated
Author: Lawrence Kirby
Date: Fri, 05 Apr 1996 00:46
Date: Fri, 05 Apr 1996 00:46
47 lines
2143 bytes
2143 bytes
In article <4jtt2c$3eq@solutions.solon.com>
news@spar.ca "USENET News System" writes:
>/* Under the Microsoft Visual C++ 2.0 and 4.0 the following
> was observed. */
>
>typedef struct { int a; int b; } *TA;
>typedef struct { int a; int b; } *TB;
Under ANSI C TA and TB have two distinct, incompatible types, they cannot
be directly assigned.
>void
>f( TA x, TB y )
>{
> f ( y, x); /* This compiles fine */
This is a constraint violation and a diagnostic is required. I tried this
with gcc and and old Microsoft version 6 compiler supplied with SCO Unix
and both generate the required diagnostic. Are you sure you're compiling in a
full ANSI C conformant mode?
>}
>
>typedef struct tagA { int a; int b; } *UA;
>typedef struct tagB { float ttt; } *UB;
>
>void
>g( UA x, UB y )
>{
> g ( y, x); /* This does not */
> /* error C2664: 'g' :
> cannot convert parameter 1 from 'struct tagB*' to 'struct tagA*' */
>}
>
>/* My question is this: Why does the function f compile without errors?
> I've tried this code in gcc and I get an error in both functions.
> Who is right here? I suspect gcc is correct, but is Microsoft's
> implemention allowable in the standard also? */
No, it isn't, at least not with the compiler options you used.
--
-----------------------------------------
Lawrence Kirby | fred@genesis.demon.co.uk
Wilts, England | 70734.126@compuserve.com
-----------------------------------------
Re: Newsgroups: comp.lang.c,comp.lang.c++,comp.lang.c++.moderated,comp.lang.c.moderated
Author: Jos A. Horsmeier
Date: Sat, 06 Apr 1996 14:40
Date: Sat, 06 Apr 1996 14:40
40 lines
1855 bytes
1855 bytes
In article <4k1qi3$5ig@solutions.solon.com>, fred@genesis.demon.co.uk wrote:
|>In article <4jtt2c$3eq@solutions.solon.com>
|> news@spar.ca "USENET News System" writes:
|>/* Under the Microsoft Visual C++ 2.0 and 4.0 the following
|> was observed. */
|>
|>typedef struct { int a; int b; } *TA;
|>typedef struct { int a; int b; } *TB;
|Under ANSI C TA and TB have two distinct, incompatible types, they cannot
|be directly assigned.
I beg to differ; Have a look at the Standard:
6.1.2.6 Compatible type and composite type
'Moreover, two structure [ ... ] types declared in two separate
translation units are compatible if they have the same number of
members, the same member names, and compatible member types; for
two structures, the members shall be in the same order [ ... ]'.
6.5.4.1 Pointer declarators
'For two pointer types to be compatible, both shall be identically
qualified and both shall be pointers to compatible types.'
[ end of quotes ]
The only thing that (still) bothers me, is the phrase 'two separate
translation units' ... Compiler checks don't span translation units ...
Does anyone has any idea what this phrase implies and what is meant
by it?
kind regards,
Jos aka jos@and.nl
--
Atnwgqkrl gy zit vgksr, ug qshiqwtzoeqs!
Re: Newsgroups: comp.lang.c,comp.lang.c++,comp.lang.c++.moderated,comp.lang.c.moderated
Author: Lawrence Kirby
Date: Sat, 06 Apr 1996 14:52
Date: Sat, 06 Apr 1996 14:52
48 lines
2313 bytes
2313 bytes
In article <4k1q2a$59u@solutions.solon.com> rdwells@mmm.com writes:
>USENET News System wrote:
>>
>> /* Under the Microsoft Visual C++ 2.0 and 4.0 the following
>> was observed. */
>>
>> typedef struct { int a; int b; } *TA;
>> typedef struct { int a; int b; } *TB;
>>
>> void
>> f( TA x, TB y )
>> {
>> f ( y, x); /* This compiles fine */
>> }
>
>I think VC++ has this right. Section 6.1.2.6 of the standard says:
>
> Moreover, two structure, union, or enumeration types declared
> in separate translation units are compatible if they have the
> same number of members, the same member names, and compatible
> member types; for two structures, the members shall be in the
> same order...
>
>Of course, this mentions "separate translation units", which
>doesn't apply in this case, so perhaps my reading is incorrect.
Correct, it doesn't apply in this case. That statement in the standard allows
you to write:
extern struct { int a; int b} A;
in 2 translation units. For that to be well defined the type of A in each
translation unit must be compatible.
However in the original case TA and TB are in the same translation unit
and do not have compatible type.
'Compatible' doesn't really correspond to anything very tangible. You just
have to follow the rules to determine whether 2 types are compatible.
'Compatibility' is then used by other parts of the standard to define
whether particular constructs are legal or not.
--
-----------------------------------------
Lawrence Kirby | fred@genesis.demon.co.uk
Wilts, England | 70734.126@compuserve.com
-----------------------------------------
Re: Newsgroups: comp.lang.c,comp.lang.c++,comp.lang.c++.moderated,comp.lang.c.moderated
Author: Michael Smith
Date: Sat, 06 Apr 1996 15:02
Date: Sat, 06 Apr 1996 15:02
53 lines
2400 bytes
2400 bytes
USENET News System wrote:
>
> /* Under the Microsoft Visual C++ 2.0 and 4.0 the following
> was observed. */
>
> typedef struct { int a; int b; } *TA;
> typedef struct { int a; int b; } *TB;
>
> void
> f( TA x, TB y )
> {
> f ( y, x); /* This compiles fine */
> }
>
> typedef struct tagA { int a; int b; } *UA;
> typedef struct tagB { float ttt; } *UB;
>
> void
> g( UA x, UB y )
> {
> g ( y, x); /* This does not */
> /* error C2664: 'g' :
> cannot convert parameter 1 from 'struct tagB*' to 'struct tagA*' */
> }
>
> /* My question is this: Why does the function f compile without errors?
> I've tried this code in gcc and I get an error in both functions.
> Who is right here? I suspect gcc is correct, but is Microsoft's
> implemention allowable in the standard also? */
>
The Microsoft compilers are generally liberal, and often let you get away
with things that you ought not. Why does it object on the second
example?
Are "int"s 16 or 32 bits in VC++ 2.0?
If they are 32 bits, it may be that the compiler objects because the
structs are of different sizes. If they are 16 bits, it may be a problem
with byte alignment.
I compiler the code under VC++ 1.0 and it compiled OK but gave warnings
about "Inderection to different types" in both subroutines.
I suppose that a more interesting question is "what are you actually
trying to achieve? There are probably many ways to work around what you
are trying to do, but a little context would help.
--
#####################################################################
Michael Smith msmith@mpx.com.au
Emmenjay Consulting http://www.hutch.com.au/~emmenjay
#####################################################################
Re: Newsgroups: comp.lang.c,comp.lang.c++,comp.lang.c++.moderated,comp.lang.c.moderated
Author: Dan Pop
Date: Mon, 08 Apr 1996 16:11
Date: Mon, 08 Apr 1996 16:11
49 lines
2604 bytes
2604 bytes
In <4k5vop$9ub@solutions.solon.com> jos@and.nl (Jos A. Horsmeier) writes:
>In article <4k1qi3$5ig@solutions.solon.com>, fred@genesis.demon.co.uk wrote:
>|>In article <4jtt2c$3eq@solutions.solon.com>
>|> news@spar.ca "USENET News System" writes:
>
>|>/* Under the Microsoft Visual C++ 2.0 and 4.0 the following
>|> was observed. */
>|>
>|>typedef struct { int a; int b; } *TA;
>|>typedef struct { int a; int b; } *TB;
>
>|Under ANSI C TA and TB have two distinct, incompatible types, they cannot
>|be directly assigned.
>
>I beg to differ; Have a look at the Standard:
As far as I can tell, compiler implementors treat TA and TB as
distinct, incompatible types.
>6.1.2.6 Compatible type and composite type
>
>'Moreover, two structure [ ... ] types declared in two separate
>translation units are compatible if they have the same number of
>members, the same member names, and compatible member types; for
>two structures, the members shall be in the same order [ ... ]'.
This paragraph doesn't say that two structure types declared in the
same translation unit can be compatible under a certain set of
circumstances. Hence it cannot be used to support your position.
>The only thing that (still) bothers me, is the phrase 'two separate
>translation units' ... Compiler checks don't span translation units ...
>Does anyone has any idea what this phrase implies and what is meant
>by it?
I think I do. Let's assume that FILE is defined as a structure in
<stdio.h>. The text you quoted guarantees that the type FILE in
translation unit A is compatible with the type FILE in translation unit B,
assuming that <stdio.h> is part of both translation units. Thus, you
can pass a FILE * argument in T.U. A to a function expecting a FILE *
argument which is defined in T.U. B without invoking undefined behaviour.
Dan
--
Dan Pop
CERN, CN Division
Email: danpop@mail.cern.ch
Mail: CERN - PPE, Bat. 31 R-004, CH-1211 Geneve 23, Switzerland
Re: compatibility
Author: Chris Torek
Date: Mon, 08 Apr 1996 16:20
Date: Mon, 08 Apr 1996 16:20
60 lines
3340 bytes
3340 bytes
(Anyone have any idea why the `subject' line was:
Newsgroups: comp.lang.c,comp.lang.c++,comp.lang.c++.moderated,
comp.lang.c.moderated
? Not that this is a sensible set of newsgroups anyway.)
[Broken news software causes posts to moderated newsgroups to be re-wrapped
in a normal mail header, which I don't always catch and edit out. -mod]
In article <4jtt2c$3eq@solutions.solon.com> someone whose name was
destroyed by the site's own news software asked about type compatibility
for:
>>>typedef struct { int a; int b; } *TA;
>>>typedef struct { int a; int b; } *TB;
In article <4k1qi3$5ig@solutions.solon.com> fred@genesis.demon.co.uk wrote:
>>Under ANSI C TA and TB have two distinct, incompatible types, they cannot
>>be directly assigned.
In article <4k5vop$9ub@solutions.solon.com> Jos A. Horsmeier <jos@and.nl>
answered:
>I beg to differ ... [see section 6.1.2.6]
>The only thing that (still) bothers me, is the phrase 'two separate
>translation units' ... Compiler checks don't span translation units ...
>Does anyone has any idea what this phrase implies and what is meant
>by it?
The rule in 6.1.2.6 (3.1.2.6 in my ANSI edition) defines what is
usually referred to as `structural equivalence'. Many languages
define type-compatibility in terms of either `name' or `structural'
equivalence. Of these, name equivalence is the more restrictive:
it says that two types T1 and T2 are the same if and only if T1
and T2 are the same type *name*. Structural equivalence is more
generous, saying that T1 and T2 are equivalent if everything `inside'
types T1 and T2 are equivalent (recursively), i.e., T1 and T2 are
the same type *layout*.
C uses `name equivalence', but C also has separate compilation,
and does not require that type-names persist outside a compilation
unit (`source file' after #include processing). This leads to an
obvious problem: there are no names with which to demand equivalency.
Enter 6.1.2.6: since there are no names, the C standard calls for
structural equivalence.
The result is that TA and TB are compatible only in separate
compilation units. Within a single unit, they are incompatible.
Any C compiler that fails to emit a diagnostic for the original
sample program (not shown here) is not conformant.
Note that C's definition of a `type name' is a little unusual. In
most languages that use name equivalence, the language's `typedef'
construct (however it is spelled) makes a new name, and hence a new
type. (I understand Ada lets you choose whether it is a new type or
a new name for an old type.)
The rules could well be different for C++ (I am no C++ expert),
since C++ demands more from the linker.
--
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA Domain: torek@bsdi.com +1 510 234 3167
Re: compatibility
Author: Kazimir Kylheku
Date: Tue, 09 Apr 1996 03:35
Date: Tue, 09 Apr 1996 03:35
61 lines
2985 bytes
2985 bytes
In article <4kbebh$eid@solutions.solon.com>,
Chris Torek <torek@bsdi.com> wrote:
>C uses `name equivalence', but C also has separate compilation,
Not quite. C uses name equivalence for struct and union components of type
expressions.
>and does not require that type-names persist outside a compilation
>unit (`source file' after #include processing). This leads to an
>obvious problem: there are no names with which to demand equivalency.
>Enter 6.1.2.6: since there are no names, the C standard calls for
>structural equivalence.
>
>The result is that TA and TB are compatible only in separate
>compilation units. Within a single unit, they are incompatible.
>Any C compiler that fails to emit a diagnostic for the original
>sample program (not shown here) is not conformant.
Neat huh?
>Note that C's definition of a `type name' is a little unusual. In
>most languages that use name equivalence, the language's `typedef'
>construct (however it is spelled) makes a new name, and hence a new
>type. (I understand Ada lets you choose whether it is a new type or
A new typedef name does not introduce a new type. A new structure or union
_tag_ makes a unique structure or union type. Otherwise, structure equivalence
applies.
>a new name for an old type.)
One object declared to be of type (char (*)[10]) is perfectly compatible with
another object of type (char (*)[10]) based on the equivalence of the
constructed types. If I make two typedef names for the same type constructing
expression, they are compatible (unless one expression uses a structure or
union with a different tag).
struct foo { int x; int y; };
struct bar { int x; int y; };
typedef struct foo TA; /* TA and TB are compatible */
typedef struct foo TB;
typedef struct bar TC; /* TC is not compatible with TA or TB */
I could extend the type constructions for TA and TB to arbitrary complexity,
and they would still retain compatibility---due to the structure equivalence of
types:
typedef struct foo (* (*TA)())[3];
typedef struct foo (* (*TB)())[3];
typedef struct bar (* (*TC)())[3];
It still holds that TA and TB are equivalent, and TC is not compatible with
either, but now this fact can no longer be attributed to structure tag name
equivalence alone, but to the structural equivalence of the complex type
expressions _together_ with structure tag name equivalence.
--
Re: compatibility
Author: Chris Torek
Date: Tue, 09 Apr 1996 22:42
Date: Tue, 09 Apr 1996 22:42
125 lines
5404 bytes
5404 bytes
In article <4kbebh$eid@solutions.solon.com> I wrote:
>>C uses `name equivalence', but C also has separate compilation,
In article <4kclti$ong@solutions.solon.com>
Kazimir Kylheku <c2a192@ugrad.cs.ubc.ca> adds:
>Not quite. C uses name equivalence for struct and union components
>of type expressions.
I think I know what you mean here: specifically, that the other
`constructed' types (e.g., `pointer to array 10 of char') use
structural equivalence. This is true enough, but I think is not
really the best way to understand it.
It might be more accurate simply to point out that there is no `name'
(or only one, the `constructed' name) for this type. If the type is
`char (*)[10]', its type is, well, `char (*)[10]'.
>A new typedef name does not introduce a new type.
Yes, but (as I noted) this is unusual in language design. Usually,
if you say `TYPE a = INTEGER' and `TYPE b = INTEGER', types a and
b are different, even though they are clearly `the same' underneath.
In C, if you write:
typedef char (*some_name)[10];
you have made `some_name' merely an alternative name (often called
an `alias') for the existing type whose `true name' is `char (*)[10]'.
>A new structure or union _tag_ makes a unique structure or union type.
This is not strictly accurate either. It is the structure definition,
not the tag, that makes the new type:
struct foo { int x; int y; } one;
struct { int x; int y; } two;
The variable named `two' has a type whose `true name' is a struct
with no tag. No other variables of this type can ever be declared
(though a separate source file can declare a type and/or variables
that have compatible type!).
Furthermore, if you write:
typedef struct { int x; int y; } my_name;
you have merely created an alternative name for this unnamed struct
tag. The `true name' of this type is un-name-able (it is a `struct'
with no tag, as before), but the alias provides a way to name the
unnameable. A structure whose type is the type that `my_name' is
an alias for (whew!) is not compatible with a `struct foo' such as
`one', nor is it type-compatible with the variable `two' (whose type
is completely unnameable), at least not in a single compilation unit.
Ultimately, of course, the way it `really works' in a C compiler
is that each type is allocated in some sort of structure or table
or something. We can imagine a simplified version that looks
like this:
struct c_type {
enum {
ct_prim, /* primitive type, e.g., int */
ct_array, /* array N */
ct_ptr, /* pointer */
ct_stun /* structure or union */
} ct_type;
union {
int type_number; /* if prim or stun */
unsigned array_size; /* if array */
unsigned member_off; /* if memb */
} ct_un;
/*
* If the type is an array, ptr, or st/un, we need
* to know what it is an array of, ptr to, or contains.
*/
struct c_type *ct_next; /* if array, ptr, or st/un */
};
/* Exercise for the reader: figure out what is missing.
Think about const and volatile, and scoping. :-) */
A typedef just points the new type-alias to the existing `struct
c_type', or copies these fields (we would copy, rather than point
to, if we had a `char *ct_name' field, for instance). A struct or
union defininition -- that is, a struct/union keyword, followed by
an optional tag, followed by an open brace, or a struct/union
keyword, followed by a tag that has not yet been defined -- allocates
a new `struct c_type' and gives it the next `type_number', e.g.,
struct c_type *
new_stun() {
static int nextstun;
struct c_type *ct;
ct = emalloc(sizeof *ct);
ct->ct_type = ct_stun;
ct->ct_un.type_number = nextstun++;
ct->ct_next = NULL;
return ct;
}
and two structure types are compatible iff ct1->ct_un.type_number ==
ct2->ct_un.type_number.
This also handles the rather tricky case of a type whose name goes out
of scope, including type names whose scope is only a prototype, such as:
extern void f(struct { long l; } *);
Here we have defined a new (unnamed) structure type (whose `true type'
is whatever nextstun++ was), not given it a tag, not given it a typedef,
and then cut off all possible access to the already-inaccessible name
by ending the scope in which it was defined. This example is pointless,
but it illustrates what happens with:
extern void g(struct S *);
If the name `struct S' has not been mentioned before, `S' is a new
tag, and the compiler gets a `new_stun' for it. The scope of this
`struct S' tag ends at the end of the prototype, so that while
`struct S' is a perfectly good name, you can no longer use it to
get at this particular `ct->ct_un.type_number'.
--
In-Real-Life: Chris Torek, Berkeley Software Design Inc
El Cerrito, CA Domain: torek@bsdi.com +1 510 234 3167
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads