Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 4.14 on page 91

Define a macro swap(t,x,y) that interchanges two arguments of type t . (Block structure will help.)



Solution by Gregory Pietsch

Here are Greg's solutions for Cat 0 and Cat 1:

/* EXERCISE 4-14 Gregory Pietsch */

/* conditional compilation added by RJH */

#ifdef CATEGORY_0

#define swap(t,x,y) do{t z=x;x=y;y=z}while(0)

#else
#ifdef CATEGORY_1

/*
This works if I can use the assignment operator on type t.
I didn't know if I was allowed to use sizeof or not and still remain
Level 0, otherwise this one is better:
*/

#define swap(t,x,y)                             do {                                                (unsigned char *)a=(unsigned char *)(&(x));     (unsigned char *)b=(unsigned char *)(&(y));     size_t i = sizeof(t);                           while (i--) {                                       *\
a ^= *b;                                       *\
b ^= *a;                                       *\
a ^= *b;                                       a\
++;                                            b\
++;                                        }    \
                                       } while (\
0)

#endif
#endif

/* editor's note: sizeof is first mentioned on p91, after this exercise,
 * and is not explained properly until p135, so it can be used in
 * [[K&R2 solutions:Ancillary:Category numbers|Category 0]] solutions only for exercises 6-1 onward.
 */



...and here is yet another solution from Gregory:

#define swap(t,x,y)                                     \
do {                                                    \
    (unsigned char *)_0=(unsigned char *)(&(x));        \
    (unsigned char *)_1=(unsigned char *)(&(y));        \
    unsigned long _2 = (unsigned long)                  \
       ((unsigned char *)(&(x)+1)                       \
        - (unsigned char *)(&(x)));                     \
    while (_2--) {                                      \
        *_0 ^= *_1;                                     \
        *_1 ^= *_0;                                     \
        *_0 ^= *_1;                                     \
        _0++;                                           \
        _1++;                                           \
    }                                                   \
} while (0)

Solution by Lars Wirzenius Liangming

...and here is a lively entry for Category 0, from Lars, which uses token pasting to derive a name for the temporary variable:

/*
 * Solution to exercise 4-14 in K&R2, page 91:
 *
 *	Define a macro swap(t,x,y) that interchanges two arguments of type t.
 *	(Block structure will help.)
 *
 * Feel free to modify and copy, if you really must, but preferably not.
 * This is just an exercise in preprocessor mechanics, not an example of
 * how it should really be used. The trickery is not worth it to save three
 * lines of code.
 *
 * To exchange the values of two variables we need a temporary variable and
 * this one needs a name. Any name we pick, the user of the macro might also
 * use. Thus, we use the preprocessor argument concatenation operator ## to
 * create the name from the actual variable names in the call. This guarantees
 * that the result won't be either of the actual arguments. In order to
 * make sure the result also does not fall into the implementation's name
 * space, we prefix the name with something safe.
 *
 * Lars Wirzenius <liw@iki.fi>
 */

#include <stdio.h>

#define swap(t, x, y) 	do { 		t safe ## x ## y; 		safe ## x ## y = x; 		x = y; 		y = safe ## x ## y; 	} while (0)

int main(void) {
	int ix, iy;
	double dx, dy;
	char *px, *py;
	
	ix = 42;
	iy = 69;
	printf("integers before swap: %d and %d\n", ix, iy);
	swap(int, ix, iy);
	printf("integers after swap: %d and %d\n", ix, iy);
	
	dx = 123.0;
	dy = 321.0;
	printf("doubles before swap: %g and %g\n", dx, dy);
	swap(double, dx, dy);
	printf("integers after swap: %g and %g\n", dx, dy);
	
	px = "hello";
	py = "world";
	printf("pointers before swap: %s and %s\n", px, py);
	swap(char *, px, py);
	printf("integers after swap: %s and %s\n", px, py);

	return 0;
}


Solution by Sam Smolkin

Simple Category 0 Solution ;) ..Generic Programming through Macros

#define SWAP(t,x,y)  void swap(t x,t y)                                           \
                                        {t temp;                                   \
                                         temp=x;                                    \
                                         x=y;                                        \
                                         y=temp;                                      \
                                        }


Macro Call would be like this SWAP(int,x,y) SWAP(double,x,y)


Solution by menonsahab

/* It is easy to write up a solution to the above problem, if the macro is being called with just one data type.
But that is not the intention of the question. Had it been so, we could've written just a function. I see a few
bugs in the solutions of my comrades here. And the problem revolves around the namespace conflict issue raised
by Lars. He has created a good main() function and I believe that the correct solution to this problem must clearly
handle Lars's main() function, pasted below for reference:

int main(void) {
	int ix, iy;
	double dx, dy;
	char *px, *py;
	
	ix = 42;
	iy = 69;
	printf("integers before swap: %d and %d\n", ix, iy);
	swap(int, ix, iy);
	printf("integers after swap: %d and %d\n", ix, iy);
	
	dx = 123.0;
	dy = 321.0;
	printf("doubles before swap: %g and %g\n", dx, dy);
	swap(double, dx, dy);
	printf("integers after swap: %g and %g\n", dx, dy);
	
	px = "hello";
	py = "world";
	printf("pointers before swap: %s and %s\n", px, py);
	swap(char *, px, py);
	printf("integers after swap: %s and %s\n", px, py);

	return 0;
}
*/

/* Sammy's solution:
There are two problems here:
1) The function will swap the local variables, but the result will not be reflected back because the arguments 
are being passed "by value" and not "by reference".
2) The code will create a redeclaration error for the swap function if a different data type is passed as an argument.
*/

/* Lars's solution:
The solution seems nice. The problem revolves around the temporary variable. Specifically there are two problems:
1) What if the name that we choose for the temporary variable is already being used.
2) Assuming that we select a fixed name for the temporary variable, the swap() will work fine for the first time. 
But when the next swap is encountered with a different data type, it will show an error because the code will attempt
to change the storage class of a variable that has already been defined.

Also let me just add that I'm not a big fan of the do-while loop. You can always use '\' to have a multi line block.

Lars has made a great attempt to solve the above mentioned problems to a great extent. But his code breaks at a very
simple input such as:
swap(double, 53.24, 78.99)
This is because you can't append a floating point number in the name of a variable.
I have a simple workaround for this. Avoid using a temporary variable. */

#define swap(t, x, y) \	
	x = x + y; \ 	
	y = x - y; \	
	x = x - y; 

/* The above code solves all problems related to the creation of the temporary variable.
It works for chars, ints, floats and doubles. In fact the data type does not need to be passed as an argument.
The only drawback of this function is that it won't work for pointers. */

Solution by Apoorva Anand

/* Here's a simple solution that works for all cases mentioned by menonsahab. The key really is to pay attention
to the hint given in the book ("Block structure will help") */

#include <stdio.h>

#define swap(t, x, y) { t temp; temp = x, x = y, y = temp; }

int main()
{
  double a = 100.5;
  double b = 200.9;
  double p = 3.03;
  double q = 4.04;
  int x = 42;
  int y = 245;
  char *px, *py;
  px = "hello";
  py = "world";
  printf("a, b BEFORE = %f %f\n", a, b);
  printf("p, q BEFORE = %f %f\n", p, q);
  printf("x, y BEFORE = %d %d\n", x, y);
  printf("px, py BEFORE = %s %s\n", px, py);
  swap(int, x, y);
  swap(double, a, b);
  swap(double, p, q);
  swap(char *, px, py);
  printf("a, b AFTER = %f %f\n", a, b);
  printf("p, q AFTER = %f %f\n", p, q);
  printf("x, y AFTER = %d %d\n", x, y);
  printf("px, py AFTER = %s %s\n", px, py);
  return 0;
}

/* All of the previous problems with the other solutions are gone because of the block structure. Because t temp is placed
inside a block, it is an automatic variable whose scope is limited to that block. That means, multiple data types can call
swap with no issues. Sammy's solution is very similar to this, but like menonsahab states, the calling of the function swap
without making it "call by reference" makes it fail. This solution also works for pointers. */
Personal tools