help on type compatibility?

A

Ark

Hello NG,

My Lint and my compiler disagree on whether this is valid code:

typedef int test_t(char *);
typedef int contest_t(const char *);
extern contest_t somefunction;
test_t *mypointer = somefunction;

I think the assignment is clean in what it does, but who cares? - What's
the verdict of the standard? (Both C90 and C99 resolutions are greatly
appreciated.)

Thank you,
- Ark
 
A

aegis

Ark said:
Hello NG,

My Lint and my compiler disagree on whether this is valid code:

typedef int test_t(char *);
typedef int contest_t(const char *);
extern contest_t somefunction;
test_t *mypointer = somefunction;

I think the assignment is clean in what it does, but who cares? - What's
the verdict of the standard? (Both C90 and C99 resolutions are greatly
appreciated.)

Part of one of the clauses of 3.5.4.3 states:
[1] "For two function types to be compatible, both
shall specify compatible return types. Moreover,
the parameter type lists, if both are present, shall
agree in the number of parameters and in use of
the ellipsis terminator; corresponding parameters
shall have compatible types."

So we need to ascertain whether or not their parameters
have compatible type:

'Is char * compatible with const char *?'

3.5.4.1 from one of its clauses, states:

"For two pointer types to be compatible, both
shall be identically qualified and both shall
be pointers to compatible types."

So their compatibility is predicated upon
whether or not they
1) point to compatible types
2) share the same qualifications

2 does not hold here, and therefore the
types are not compatible. The implication
is then that [1] does not hold.
 
A

Ark

aegis said:
Ark said:
Hello NG,

My Lint and my compiler disagree on whether this is valid code:

typedef int test_t(char *);
typedef int contest_t(const char *);
extern contest_t somefunction;
test_t *mypointer = somefunction;

I think the assignment is clean in what it does, but who cares? - What's
the verdict of the standard? (Both C90 and C99 resolutions are greatly
appreciated.)


Part of one of the clauses of 3.5.4.3 states:
[1] "For two function types to be compatible, both
shall specify compatible return types. Moreover,
the parameter type lists, if both are present, shall
agree in the number of parameters and in use of
the ellipsis terminator; corresponding parameters
shall have compatible types."

So we need to ascertain whether or not their parameters
have compatible type:

'Is char * compatible with const char *?'

3.5.4.1 from one of its clauses, states:

"For two pointer types to be compatible, both
shall be identically qualified and both shall
be pointers to compatible types."

So their compatibility is predicated upon
whether or not they
1) point to compatible types
2) share the same qualifications

2 does not hold here, and therefore the
types are not compatible. The implication
is then that [1] does not hold.
Thank you, aegis.
But...
Is it indeed that char * is not compatible with const char * ?
char * is unqualified pointer to char; const char * is unqualified
pointer to const char. So the question reduces to whether char is
compatible with const char, and I think it is... After all, we routinely
write
char c;
const char cc;
char *p;
const char *cp;
..............
c = cc;
cp = p;
I beg for further clarification...
Thank you,
Ark
 
R

Robin Haigh

Ark said:
Hello NG,

My Lint and my compiler disagree on whether this is valid code:

typedef int test_t(char *);
typedef int contest_t(const char *);
extern contest_t somefunction;
test_t *mypointer = somefunction;

I think the assignment is clean in what it does, but who cares? - What's
the verdict of the standard? (Both C90 and C99 resolutions are greatly
appreciated.)

The assignment is legal because it's portable to convert a function pointer
to a different function pointer type and back again. (This can avoid the
need to use unions of function pointer types, which would be pointlessly
clumsy.)

But it's well worth a warning at this point, because no warning can be given
later if you then call the function through the pointer of the wrong type,
and that would produce undefined behaviour.

The problem is that the caller and the callee must agree implicitly on all
the details of how the function call is actually set up. The compiler makes
its own rules about that, and it's allowed to refer to the types of the
arguments, including qualifiers, if there's a prototype. So calling through
the wrong prototype can produce a mismatch in the calling sequence.

In this case it's likely that the undefined behaviour produced is to do the
right thing on all implementations, unless somebody knows different...
 
P

pete

Ark said:
char * is unqualified pointer to char; const char * is unqualified
pointer to const char.

No.

N869
6.2.5 Types
[#26]
Each
unqualified type has several qualified versions of its
type, corresponding to the combinations of one, two, or
all three of the const, volatile, and restrict qualifiers.
 
C

Chris Torek

Is it indeed that char * is not compatible with const char *?

Yes, it is indeed not compatible.
[yet] we routinely write
char *p;
const char *cp;
.............
cp = p;
I beg for further clarification...

There is a special rule for assignment (including the implied
assignment from prototyped function calls) that relaxes the
constraints, so that you can do "cp = p", but not "p = cp".

This special rule does *not* apply to other cases, so:

char **pp;
...
pp = &cp;

requires a diagnostic.
 
C

Chris Torek

The assignment is legal because ...

The assignment requires a diagnostic. GCC (some versions anyway)
just gets this wrong.

I think you are thinking of the version with casts:

test_t *mypointer = (test_t *)somefunction;

which does not require a diagnostic.
The problem is that the caller and the callee must agree implicitly on all
the details of how the function call is actually set up.

This, however, is correct.

The Standard has some wording that implies (or perhaps even "requires"
if you believe the footnotes, but the footnotes are "non-normative",
meaning an Evil Implementor can violate them without breaking the
rules) ... er, where was I? Oh yes: qualified and unqualified
variants of types are *supposed* to have the same underlying
representations, so one might simply assume that test_t and contest_t
are effectively interchangeable, and get away with it. It is quite
unlikely to break. Still, it is best not to skate on the thin ice.
 
J

Jordan Abel

The assignment requires a diagnostic. GCC (some versions anyway)
just gets this wrong.

I think you are thinking of the version with casts:

test_t *mypointer = (test_t *)somefunction;

which does not require a diagnostic.


This, however, is correct.

The Standard has some wording that implies (or perhaps even "requires"
if you believe the footnotes, but the footnotes are "non-normative",
meaning an Evil Implementor can violate them without breaking the
rules)

Even if the footnote is prescribing an interpretation of normative text,
rather than providing new rules?
 
K

kuyper

aegis wrote:
....
'Is char * compatible with const char *?'

3.5.4.1 from one of its clauses, states:

"For two pointer types to be compatible, both
shall be identically qualified and both shall
be pointers to compatible types."

So their compatibility is predicated upon
whether or not they
1) point to compatible types
2) share the same qualifications

2 does not hold here, and therefore the
types are not compatible.

I don't follow that: neither pointer type is qualified, so item [2]
holds. Only the pointed-at types are differently qualified. The pointer
types themselves are both unqualified.
The implication is then that [1] does not hold.

I don't follow that implication. You're right that [1] doesn't hold.
However, that's because 6.7.3p9 requires the two types to have the same
qualification in order to be compatible, and "char" has different
qualification than "const char". It has nothing to do with the fact
that 3.5.4.1 requires the pointer types themselves to be identically
qualified.
 
D

Douglas A. Gwyn

Chris said:
... so that you can do "cp = p", but not "p = cp".

Of course in practice you can accomplish the effect by
using a cast. At least that documents that something
unusual (and perhaps incorrect) is going on.
 
D

Douglas A. Gwyn

Chris said:
... the footnotes are "non-normative",
meaning an Evil Implementor can violate them without breaking the
rules) ...

No that's not what it means. The footnotes (and examples)
are not meant to impose any additional requirements, but
they can and do clarify what is meant by otherwise puzzling
requirements in the normative portion of the text.
 
K

Keith Thompson

Douglas A. Gwyn said:
No that's not what it means. The footnotes (and examples)
are not meant to impose any additional requirements, but
they can and do clarify what is meant by otherwise puzzling
requirements in the normative portion of the text.

But they do not always do so perfectly.

For example, 6.2.5p26 says:

A pointer to void shall have the same representation and alignment
requirements as a pointer to a character type.

and a footnote says:

The same representation and alignment requirements are meant to
imply interchangeability as arguments to functions, return values
from functions, and members of unions.

Without that footnote, an implementation that passed void* and char*
arguments in different registers (as some real implementations do for
integer and floating-point arguments) could be conforming; the types
would still have the same representation and alignment requirements.

There would be little reason (that I can think of) for an
implementation to pass char* and void* differently, but as a
programmer I wouldn't be able to assume that they weren't if I wanted
my code to be portable.
 
K

kuyper

Douglas said:
No that's not what it means. The footnotes (and examples)
are not meant to impose any additional requirements,

The point is, that not only are they not meant to impose any additional
requirements, they CAN"T impose any additional requirements. A footnote
can guide us to the correct interpretation of the requirements, but if
a footnote claims that something is true which cannot be derived from
the normative text, that claim is simply false, and can therefore be
ignored.
 
W

WaterWalk

I'm sorry, but what does "typedef int test_t(char *)" mean?

int test_t(char *); //This is a function prototype
typedef int (*test_t)(char *); //This is a fuction pointer typedef

But,
typedef int test_t(char *); // I don't know what it is.
 
P

pete

WaterWalk said:
I'm sorry, but what does "typedef int test_t(char *)" mean?

int test_t(char *); //This is a function prototype

When you have a declaration
with a primary expression identifier,
and you write "typedef" in front of it,
what was the identifier in the declaration,
becomes a name for the type that it had.

That typedef is for a function type.

/* BEGIN new.c */

#include <stdio.h>

typedef int test_t(char *);

test_t test1, test2;

int main(void)
{
test1("Hello");
test1("World");
return 0;
}

int test1(char *a)
{
return puts(a);
}

int test2(char *a)
{
return puts(a);
}

/* END new.c */
 
P

pete

pete wrote:
typedef int test_t(char *);

test_t test1, test2;

int main(void)
{
test1("Hello");
test1("World");

I intended that last line to be test2("World")
but it doesn't make much difference.
 
B

Ben Bacarisse

Is it indeed that char * is not compatible with const char *?

Yes, it is indeed not compatible.
[yet] we routinely write
char *p;
const char *cp;
.............
cp = p;
I beg for further clarification...

There is a special rule for assignment (including the implied assignment
from prototyped function calls) that relaxes the constraints, so that you
can do "cp = p", but not "p = cp".

This special rule does *not* apply to other cases, so:

char **pp;
...
pp = &cp;

requires a diagnostic.

I some ways the more surprising example is that:

const char * const *pcp;
pcp = pp;

also requires a diagnostic. Surprising because this way round no
"const"ness is being lost.
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top