[comp.lang.c] passing *char parameters by reference

gls@novavax.UUCP (Gary Schaps) (08/10/89)

Would someone be kind enough to tell me why this program fails?

char *x, *y;
{
   register char *temp;

   temp = x;
   x = y;
   y = temp;
}

main()
{
   char *a="aaaa";
   char *b="bbbb";

   swap( &a, &b );
   printf( " a = %s\t b = %s\n", a, b);
}

cpcahil@virtech.UUCP (Conor P. Cahill) (08/10/89)

In article <1424@novavax.UUCP>, gls@novavax.UUCP (Gary Schaps) writes:

I assume there is a line here that says:
  swap(x,y)
> char *x, *y;
> {
>    register char *temp;
> 
>    temp = x;
>    x = y;
>    y = temp;
> }
> 
> main()
> {
>    char *a="aaaa";
>    char *b="bbbb";
> 
>    swap( &a, &b );
>    printf( " a = %s\t b = %s\n", a, b);
> }


1.  You are calling swap with the & (address of) a character pointer.
    This is a pointer to a pointer.  Therefore the swap function must
    be written as follows:

	void
	swap(x,y)
		char **x;
		char **y;
	{
		retister char *temp;
		temp = *x;
		*x = *y;
		*y = temp;
	}
	

matthew@sunpix.UUCP ( Sun Visualization Products) (08/10/89)

In article <1424@novavax.UUCP>, gls@novavax.UUCP (Gary Schaps) writes:
| Would someone be kind enough to tell me why this program fails?
| 
| char *x, *y;
| {
|    register char *temp;
| 
|    temp = x;
|    x = y;
|    y = temp;
| }
| 
| main()
| {
|    char *a="aaaa";
|    char *b="bbbb";
| 
|    swap( &a, &b );
|    printf( " a = %s\t b = %s\n", a, b);
| }


Sure!!  

First off you have two functions, but only one has a name. Since 'main()' 
calls a function 'swap()', and the un-named function looks like a swap 
function, You need to prefix the function with a:

	swap( x, y )



Secondly, and where your biggest problem is, is in the swapping of pointers.

1) main correctly passes the address of the pointers a and b to the 
   swap() funtions. (or in other words main is passing "pointers to 
   pointer" to swap().

2) The swap() function is then swapping the pointers to pointers around. 
   The effect 'pointer x is now pointing to pointer b', and 'pointer y 
   is now pointing to pointer a'.  The values of pointers a and b have 
   not been swapped, just the pointers to the pointers.  I.E.:

     x -> a -> "aaaa"    becomes     y -> a -> "aaaa"
     y -> b -> "bbbb"    becomes     x -> b -> "bbbb"

    (Notice: the relationship between a and "aaaa" and b and "bbbb" do not
     change).


Solution: Add another level of indirection to swap.  Make the variables
    x and y into pointer to pointers (i.e.: **x and **y) then swap the
    contents of the whats being pointed to by x (*x) with the contents
    of whats being pointed to by y (*y).  Below is a correct swap routine.
    (Notice: Since no mathimatical operations are being done on these
     pointers to pointers, I've taken the liberty of converting them
     to type 'void', since pointer type is only important when doing
     mathimatical operations on the pointers themselves.)


          
          void swap(x,y)          /* Per K&R edition 1, page 117 */
          void **x, **y;          /* Substituted **x for *x[]    */
          {
             register void *temp;
          
             temp = *x;
             *x = *y;
             *y = temp;
          }


  (Authors note: Code tested on a Sun 3/50 workstation running SunOS 4.0.1)


-- 
Matthew Lee Stier                            |
Sun Microsystems ---  RTP, NC  27709-3447    |     "Wisconsin   Escapee"
uucp:  sun!mstier or mcnc!rti!sunpix!matthew |
phone: (919) 469-8300 fax: (919) 460-8355    |

brian@trantext.UUCP (Brian Bainter) (08/11/89)

In article <1424@novavax.UUCP>, gls@novavax.UUCP (Gary Schaps) writes:
> Would someone be kind enough to tell me why this program fails?
> 
> char *x, *y;
> {
[code to swap strings deleted]
> }
> 
> main()
> {
>    char *a="aaaa";
>    char *b="bbbb";
> 
>    swap( &a, &b );
>    printf( " a = %s\t b = %s\n", a, b);
> }



What you are trying unsuccessfully to do here, if I am not mistaken, is to
swap what a and b point to. If this is the case, then leave off the ampersands
(&) from the swap call. What you are doing wrong here is trying to pass the
address of the pointers a and b to swap instead of the addresses that a and
b point to. Remember that a and b are pointers and therefore have addresses
themselves. When you use the ampersand with a variable (pointer or otherwise)
you will be working with the address of that particular variable.



Hope this helps,

-- 

Brian R. Bainter  KA7TXA
gatech!kd4nc!trantext!brian or
gatech!tomcat!brian

jeffa@hpmwtd.HP.COM (Jeff Aguilera) (08/11/89)

>> Would someone be kind enough to tell me why this program fails?

Because it is wrong.  Instead try

swap(x,y)
	register char **x, **y;
{
	register char *temp;

	temp = (*x);	/* parentheses added for clarity */
	(*x) = (*y);
	(*y) = temp;
}

main()
{
	char *a="aaaa";
	char *b="bbbb";

	swap( &a, &b );	/* Note that &(char*) produces a (char**) */
	printf( " a = %s\t b = %s\n", a, b);
}

chip@vector.Dallas.TX.US (Chip Rosenthal) (08/11/89)

In article <1424@novavax.UUCP> gls@novavax.UUCP (Gary Schaps) writes:
>Would someone be kind enough to tell me why this program fails?

Because it wasn't run through lint...

-- 
Chip Rosenthal / chip@vector.Dallas.TX.US / Dallas Semiconductor / 214-450-5337
"I wish you'd put that starvation box down and go to bed" - Albert Collins' Mom

schaut@madnix.UUCP (Rick Schaut) (08/11/89)

In article <1424@novavax.UUCP> gls@novavax.UUCP (Gary Schaps) writes:
>Would someone be kind enough to tell me why this program fails?

[swap(x,y)]
>char *x, *y;
>{
>   register char *temp;
>
>   temp = x;
>   x = y;
>   y = temp;
>}
>
>main()
>{
>   char *a="aaaa";
>   char *b="bbbb";
>
>   swap( &a, &b );
>   printf( " a = %s\t b = %s\n", a, b);
>}

You need to declare the parameters to swap to be pointers to pointers
to chars:

swap(x,y)
char **x,**y;
{
	register char *temp;

	temp = *x;
	*x = *y;
	*y = temp;
}

Now the call, swap(&a, &b) will work correctly.  Remember, you're
passing the pointers by reference, not the arrays, hence the extra
level of indirection.

-- 
   Richard Schaut     Madison, WI              Madison: an alternative
ArpaNet: madnix!schaut@cs.wisc.edu                      to reality.
UseNet: ...uwvax!astroatc!nicmad!madnix!schaut
             {decvax!att}!

karl@haddock.ima.isc.com (Karl Heuer) (08/12/89)

In article <750@greens.UUCP> matthew@sunpix.UUCP ( Sun Visualization Products) writes:
>(...I've taken the liberty of converting them to type 'void', since pointer
>type is only important when doing mathimatical operations on the pointers
>themselves.)

Uh, that's not quite true.

>	void swap(x,y) void **x, **y; {
>	    register void *temp;
>	    temp = *x;  *x = *y;  *y = temp;
>	}

This is a function that can be used to swap two |void *| objects.  Because of
a grandfather clause, it can also be used to swap two |char *| objects.  It is
*not* necessarily true that it can swap |int *|s, though; on some machines
|char *| and |int *| have different representations, and there's no guarantee
that they even have the same length.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

walter@hpclwjm.HP.COM (Walter Murray) (08/12/89)

>>Would someone be kind enough to tell me why this program fails?

>Because it wasn't run through lint...

If you use function prototypes, an ANSI-conforming compiler will also
catch this kind of error.

Walter Murray
----------

ping@cubmol.BIO.COLUMBIA.EDU (Shiping Zhang) (08/13/89)

In article <1424@novavax.UUCP> gls@novavax.UUCP (Gary Schaps) writes:
>Would someone be kind enough to tell me why this program fails?
>
>char *x, *y;
>{
>   register char *temp;
>
>   temp = x;
>   x = y;
>   y = temp;
>}
>
>main()
>{
>   char *a="aaaa";
>   char *b="bbbb";
>
>   swap( &a, &b );
>   printf( " a = %s\t b = %s\n", a, b);
>}

The problem is the function swap. It should be defined as following:

swap(x,y)
char **x,**y; /* x and y are points to point to char */
{
    register char *temp;
    temp = *x; /* assign temp the address x points to */
    *x = *y;   /* make x point to the address y points to */
    *y = temp; /* reassign y the address x used to point to */
}


- ping

karl@haddock.ima.isc.com (Karl Heuer) (08/14/89)

In article <660050@hpclwjm.HP.COM> walter@hpclwjm.HP.COM (Walter Murray) writes:
>If you use function prototypes, an ANSI-conforming compiler will also
>catch this kind of error.

Or it might silently "fix" it by doing an automatic conversion from the actual
argument type to the formal parameter type.  Hopefully, any reasonable ANSI C
compiler will at least have an option to warn about such constructs.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

bobmon@iuvax.cs.indiana.edu (RAMontante) (08/14/89)

A minor comment...

Among all the responses to this swap question, I think I saw _one_
that suggested getting rid of the ampersands and passing in the array
pointers (instead of the addresses of the array pointers).  All the
others leap right in and rewrite the swap routine to accept the
addresses of the pointers, at the expense of working through an
additional, unneeded level of indirection.

Do people have some objection to quick and straightforward fixes, or am
I missing something?

ping@cubmol.BIO.COLUMBIA.EDU (Shiping Zhang) (08/14/89)

In article <24555@iuvax.cs.indiana.edu> bobmon@iuvax.cs.indiana.edu (RAMontante) writes:
>A minor comment...
>
>Among all the responses to this swap question, I think I saw _one_
>that suggested getting rid of the ampersands and passing in the array
>pointers (instead of the addresses of the array pointers).  All the
>I missing something?

Getting rid of the ampersands would not work as intended. Only the
first characters would be exchanged between the two strings.


-ping

cpcahil@virtech.UUCP (Conor P. Cahill) (08/14/89)

In article <24555@iuvax.cs.indiana.edu>, bobmon@iuvax.cs.indiana.edu (RAMontante) writes:
> Among all the responses to this swap question, I think I saw _one_
> that suggested getting rid of the ampersands and passing in the array
> pointers (instead of the addresses of the array pointers).  All the
> others leap right in and rewrite the swap routine to accept the
> addresses of the pointers, at the expense of working through an
> additional, unneeded level of indirection.
> 
> Do people have some objection to quick and straightforward fixes, or am
> I missing something?

You are missing something.  The problem is
	
	1.  There are 2 pointers to character strings in the main routine
	2.  Following the execution each pointer is to point to the other
	    string.

Passing in the address of the pointer is the ONLY way.  

Another mechanism would be to copy the data to a temp space, copy over the 
first pointer, and copy from the temp space to the second pointer.  This 
would require that the data areas pointed to by both pointers is writeable 
(constant strings are not always writeable) AND is the same size.

Consider the following:

	char	* a = "01234567890";
	char	* b = "0";

The only mechanism that could swap what these pointers point to would be
to modify the pointers themselves.

walter@hpclwjm.HP.COM (Walter Murray) (08/14/89)

Karl Heuer writes:

>In article <660050@hpclwjm.HP.COM> walter@hpclwjm.HP.COM (Walter Murray) writes:
>>If you use function prototypes, an ANSI-conforming compiler will also
>>catch this kind of error.

>Or it might silently "fix" it by doing an automatic conversion from the actual
>argument type to the formal parameter type.  Hopefully, any reasonable ANSI C
>compiler will at least have an option to warn about such constructs.

Are you sure?

For a function call, the dpANS has a constraint which requires that "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."
(3.3.2.2)

Given:

   void swap (char *x, char *y) { }
   main()
   {
      char *a; char *b;
      swap( &a, &b );
   }

Each argument in the call has type "pointer to pointer to char."
The corresponding parameters have type "pointer to char."  Assignments
would not be legal (3.3.16.1).  Because a constraint has been violated,
an ANSI-conforming compiler will produce a diagnostic message.  It may
well "fix" the mistake, but I don't think it can do so silently.

Walter Murray
Not an X3J11 answer, of course
----------------------------------------------------------------------

chris@mimsy.UUCP (Chris Torek) (08/15/89)

The original question was something vague about swapping characters
and/or pointers (see <1424@novavax.UUCP>).  In many followups, people
suggest changing swap() (whose name was half-missing from the original
posting) to take `char **' arguments, etc.

In article <24555@iuvax.cs.indiana.edu> bobmon@iuvax.cs.indiana.edu
(RAMontante) writes:
>>Among all the responses to this swap question, I think I saw _one_
>>that suggested getting rid of the ampersands and passing in the array
>>pointers (instead of the addresses of the array pointers).  All the
>>I missing something?

In article <318@cubmol.BIO.COLUMBIA.EDU> ping@cubmol.BIO.COLUMBIA.EDU
(Shiping Zhang) writes:
>Getting rid of the ampersands would not work as intended. Only the
>first characters would be exchanged between the two strings.

and in <1010@virtech.UUCP> cpcahil@virtech.UUCP (Conor P. Cahill) expands
on this:
>You are missing something.  The problem is
>	
>	1.  There are 2 pointers to character strings in the main routine
>	2.  Following the execution each pointer is to point to the other
>	    string.
[details---all correct---deleted]

This is probably correct.  However, rather like the mathematician
on the train, we have seen only half a cow.  We know only half of
statement 2: following the execution of swap(), each pointer is to
point to a string (because each is, in the original program, passed
to printf to be formatted with `%s').  We can also guess (from the
routine's name, `swap') that it is to swap something.  What we do
not know is what the writer intended to swap!  The original program
contained no comments, provided no sample output, and had no
specifications at all.

Our only remaining clue is the subject line, `passing *char parameters
by reference', and it leads me to favour the versions that both Shiping
Zhang and Conor P. Cahill suggested.  But this does bring up a point
relating to more than just C:

	You cannot debug something until you know what it is
	supposed to do.

In my experience, this is the single most troublesome aspect of all
debugging, whether for software or for hardware.  This message is, I
suppose, a plea for precision.  Do not just say: `It is broken'.  Say,
rather:  `It does this, but it should do that.'  Better yet:  `When I
<action>, <object> responds by <action>; it should instead do <action>'.
You may even find that, once you formulate the proper question, the
answer becomes obvious.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris