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

Discussion in 'C Programming' started by Schnoffos, Jun 27, 2003.

1. ### SchnoffosGuest

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

Schnoffos, Jun 27, 2003

2. ### Chris TorekGuest

In article <>
Schnoffos <> writes:
>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.
--
In-Real-Life: Chris Torek, Wind River Systems (BSD engineering)
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://67.40.109.61/torek/index.html (for the moment)
Reading email is like searching for food in the garbage, thanks to spammers.

Chris Torek, Jun 27, 2003

3. ### Martien VerbruggenGuest

On 26 Jun 2003 19:29:55 -0700,
Schnoffos <> wrote:
> 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
--
|
Martien Verbruggen | Begin at the beginning and go on till you
Trading Post Australia | come to the end; then stop.
|

Martien Verbruggen, Jun 27, 2003