Behdad Esfahbod's daily notes on GNOME, Pango, Fedora, Persian Computing, Bob Dylan, and Dan Bern!

My Photo
Name:
Location: Toronto, Ontario, Canada

Ask Google.

Contact info
Google
Hacker Emblem Become a Friend of GNOME I Power Blogger
follow me on Twitter
Archives
July 2003
August 2003
October 2003
November 2003
December 2003
March 2004
April 2004
May 2004
July 2004
August 2004
September 2004
November 2004
March 2005
April 2005
May 2005
June 2005
July 2005
August 2005
September 2005
October 2005
November 2005
December 2005
January 2006
February 2006
March 2006
April 2006
May 2006
June 2006
July 2006
August 2006
September 2006
October 2006
November 2006
December 2006
January 2007
February 2007
March 2007
April 2007
May 2007
June 2007
July 2007
August 2007
September 2007
October 2007
November 2007
December 2007
January 2008
February 2008
March 2008
April 2008
May 2008
June 2008
July 2008
August 2008
October 2008
November 2008
December 2008
January 2009
March 2009
April 2009
May 2009
June 2009
July 2009
August 2009
November 2009
December 2009
March 2010
April 2010
May 2010
June 2010
July 2010
October 2010
November 2010
April 2011
May 2011
August 2011
September 2011
October 2011
November 2011
November 2012
June 2013
January 2014
May 2015
Current Posts
McEs, A Hacker Life
Monday, August 08, 2005
 Pass by Reference in C

When reading setjmp.h I figured out a nice trick used by glibc hackers to implement pass-by-reference'd data types. It was like Wow! Cool! Beautiful! Automatic memory allocation with no reference operator when passing to functions. This has been indeed part of the design of C and exactly why array types were added to C, yet I never figured out arrays can be used to implement opaque types that are passed by reference.
#include <stdio.h>

typedef struct __dummy_tag {
int x;
} the_type[1];


void
setter(the_type i, int v)
{
i->x = v;
}

int
getter(the_type i)
{
return i->x;
}


int
main(void)
{
the_type v;

setter (v, 10);
printf ("%d\n", getter (v));

setter (v, 20);
printf ("%d\n", getter (v));

return 0;
}

You learn new things everyday...

Update: Apparently blogger has !@#$ed up my post. Kindly read the code syntax-highlighted and cutely-indented in my blog.

Update2: Originally I had planet blamed instead of blogger in the previous update. Giving up after a few tries to do overstrike on planet, I simply replaced it with blogger.

Comments:
behdad, Planet did precisely what your atom feed told it to. :-)
 
I'd say it's not not much "pass-by-reference" -- you can achieve the same effect by using pointers for function parameters. The trick just allows to avoid '&' operator everywhere, and so can be used not only for parameter passing. Say, I often use this:

void string_new( string* );
void string_free( string* );
......
string s[1];
string_new(s);
........
string_free(s);


just as syntactic convenience.
 
But doing that way, you get convenience only when passing that variable as function parameter, while doing a direct assignment will require extra syntax. I think that it's a nice trick only if used with structs.
 
A slightly more readable version of the typedef would be:

typedef struct __dummy_tag {
int x;
} * the_type;

BTW, I don't like this pass-by-ref-without-ampersands trick because it obscures the intention of the code. It's not immediately obvious (other than by its name) that setter will alter the contents of v -- this can very confusing for a maintainer (not the original author of the code) who is for sure accustomed to see '&' for pass-by-reference in C and could easily overlook the side-effects of 'setter'
 
Starting from the first comment so far:

Jeff: Sorry man, fixed the notice.

Vladimir: The whole pass-by-reference thing /is/ syntactic sugar. No wonder we don't have it in C, because it's only syntactically different from passing a pointer.

Anonymous: Right. You are fine as long as you treat it as an opaque type and don't assign it. Otherwise you need to treat it like a pointer.

Next Anonymous: Man, the whole trick is to use an array of length 1 instead of a pointer. If you typedef to a pointer, then defining a variable of that type does not allocate memory for it, you have to malloc(). About obscuring, true, it breaks all hidden assumptions of a C programmer, but then it's just starting to look like C++...
 
Why

typedef struct __dummy_tag {
int x;
} the_type[1];

and not

typedef struct __dummy_tag {
int x;
} the_type[0];
 
Well, because a zero-length array does not allocate much memory (or any, for what it's worth)! I mean, you know what that 1 stands for, right?
 
I am the first Anonymous (Paolo, an italian who found this article looking for random things ;-) ).
I've tried for curiousity to use the zero sized array for this trick, and it seems to work. Size of the variable it's said to be 0, by the sizeof operator...
By the way, I agree that obscuring the pass by variable it's bad in C, in this case, because you can't do the same trick with any other type. In C++ instead they uses references to do this , and, since it works with any data type, the programmer it's (should be) more careful about doing assumptions.
 
Passing-by-reference isn't just syntactically different than passing-by-pointer. References may only refer to a valid object, and cannot be made to refer to a different object at runtime. Pointers, on the other hand, can be changed to point at any arbitrary memory address (including invalid ones). So while they're often used to achieve the same thing (avoiding a costly pass-by-value copy of a function argument), they have subtle differences. I'm sure you knew this already, but in case anybody didn't...
 
Almost true. In Java you still have null references, right?
 
The struct is unnecessary. This will do too and is even simpler:

typedef int the_type[1];

void setter(the_type i, int v)
{
i[0] = v;
}

int getter(the_type i)
{
returni[0];
}
 
roel, that has the problem raised here.
 
Post a Comment



<< Archive
<< Home