dirty stuff: f(int,int) cast to f(struct{int,int})

S

Schnoffos

Given two functions:

f (T1 a, T2 b, ... Tn z)

and

g (struct {T1 a; T2 b; ... Tn z} s)

i wonder how portable it is to call them (proper casts provided) using
the respecitve other signature, for example:

f( {a, b, .. z} );

g (a, b, ..z);

The question implies that I don't believe this is correct C at all,
but I wonder if there are compilers that crash on this.

Reasons for crashes could be different padding of structures and
argument lists, which I believe will seldom occure (if padding is used
for structures it will probably also be used for actual argument
lists). Another reason may be different ordering of arguments such
that for f(a,b,c) &a < &b is not necessarily true.

It seems probable to me, that though this is theoretically unportable
and simply invalid C code, it will in pratice work on most if not all
current compilers and platforms.

Is this correct? Can you provide me with examples where this does or
does not work?

I used the following code to test it:

--- snip ---

struct sii { char x; int y; };

void f_ii (char x, int y) { printf ("%c, %d\n", x, y); }
void f_sii (struct sii s){ printf ("%c, %d\n", s.x, s.y); }

void (*fp_ii)(char x, int y);
void (*fp_sii)(struct sii);

main ()
{
fp_ii = f_ii;
fp_ii ('1', 2);
fp_ii = (void (*)(char, int))f_sii;
fp_ii ('1', 2);
}

--- snip ---

Greetings & Thanks

PS: If you reply by mail, please replace utech.de with t-online.de
 
C

Chris Torek

Given two functions:

f (T1 a, T2 b, ... Tn z)

and

g (struct {T1 a; T2 b; ... Tn z} s)

i wonder how portable it is to call them (proper casts provided) using
the respecitve other signature, for example:

f( {a, b, .. z} );

g (a, b, ..z);

The question implies that I don't believe this is correct C at all,
but I wonder if there are compilers that crash on this.

It is not correct, and there are compilers that produce code
that fails at runtime (whether it "crashes" or does something
else is a bit hard to predict, but in any case, it does not
work).
Reasons for crashes could be different padding of structures and
argument lists, which I believe will seldom occure (if padding is used
for structures it will probably also be used for actual argument
lists). Another reason may be different ordering of arguments such
that for f(a,b,c) &a < &b is not necessarily true.

There are other possibilities, one of which is definitely used today.
It seems probable to me, that though this is theoretically unportable
and simply invalid C code, it will in pratice work on most if not all
current compilers and platforms.

Specifically, if some of the types Tn are "simple" integral
or pointer types, or even floating-point types, compilers for
Sun SPARC hardware will pass the ordinary parameters in
ordinary registers:

void f(int a, int b, int c, int d) {
/*
* any code here that uses "a" through "d"
* will refer, internally, to the machine's
* "%o0" through "%o3" registers, or (if there
* is a SAVE instruction) %i0 through %i3.
*/
...
}

In function g(), however, the (single) parameter has a structure
type. The caller will pass the address of either the original or
a copy (I forget which) and the callee will then assume that the
(single) incoming parameter has type "pointer to struct unnamed".
If the caller does not copy, the callee may have to do so, but
in any case:

struct unnamed { int a, b, c, d; };

void g(struct unnamed arg) {
/*
* any code here that uses arg.a through arg.d
* will cause the compiler to treat register %o0
* (or %i0 after a SAVE) as a pointer to a
* structure, and refer to [%o0 + 0] through
* [%o0 + 12] respectively.
*/
...
}

If you actually call g() with four integer parameters in %o0 through
%o3, g() will treat the first one as if it were a pointer. Depending
on the value, the code might then crash, or simply produce wrong
answers. If you actually pass a structure to f(), the apparent
"value" of "a" will be the low 32 bits of the address of the original
or copy (again, I forget which) structure, while the values of b
through d will be whatever happens to be lying around in %o1 through
%o3 at the time.
 
M

Martien Verbruggen

Given two functions:

f (T1 a, T2 b, ... Tn z)

and

g (struct {T1 a; T2 b; ... Tn z} s)

i wonder how portable it is to call them (proper casts provided) using
the respecitve other signature, for example:

f( {a, b, .. z} );

g (a, b, ..z);

The question implies that I don't believe this is correct C at all,
but I wonder if there are compilers that crash on this.

It isn't correct C at all.
Reasons for crashes could be different padding of structures and
argument lists, which I believe will seldom occure (if padding is used
for structures it will probably also be used for actual argument
lists). Another reason may be different ordering of arguments such
that for f(a,b,c) &a < &b is not necessarily true.

As far as C is concerned, there is no such thing as padding for an
argument list. How arguments are communicated to a function's body is
left to the implementation.
It seems probable to me, that though this is theoretically unportable
and simply invalid C code, it will in pratice work on most if not all
current compilers and platforms.

I don't agree. Maybe your idea of "most current compilers and
platforms" differs from mine (see below).
Is this correct? Can you provide me with examples where this does or
does not work?

I think I can show a few examples of this not "working", in various
manners.

Code to test (basically yours, including stdio, and fixed up main()):

#include <stdio.h>

struct sii { char x; int y; };

void f_ii (char x, int y) { printf ("%c, %d\n", x, y); }
void f_sii (struct sii s){ printf ("%c, %d\n", s.x, s.y); }

void (*fp_ii)(char x, int y);
void (*fp_sii)(struct sii);

int main(void)
{
fp_ii = f_ii;
fp_ii ('1', 2);
fp_ii = (void (*)(char, int))f_sii;
fp_ii ('1', 2);
return 0;
}

\begin{offtopic}

On linux, gcc 3.2:

$ gcc -fpack-struct -Wall -W -ansi -pedantic foo.c
$ ./a.out
1, 2
1, 33554432


On linux, Intel compiler:

$ /opt/intel/compiler70/ia32/bin/icc -Zp1 -ansi foo.c
$ ./a.out
1, 2
1, 33554432

$ /opt/intel/compiler70/ia32/bin/icc -Zp2 -ansi foo.c
$ ./a.out
1, 2
1, 131072


On Solaris, gcc 2.95.2:

$ gcc -Wall -W -ansi -pedantic foo.c
$ ./a.out
1, 2
zsh: segmentation fault ./a.out

$ gcc -O2 -Wall -W -ansi -pedantic foo.c
$ ./a.out
1, 2
zsh: bus error ./a.out


Solaris with Sun's compiler:

$ /opt/SUNWspro/bin/cc foo.c
$ ./a.out
1, 2
zsh: segmentation fault ./a.out

\end{offtopic}

Martien
 

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

Similar Threads


Members online

Forum statistics

Threads
473,734
Messages
2,569,441
Members
44,832
Latest member
GlennSmall

Latest Threads

Top