Tag Archives: c

Trying Too Hard

In a post titled The Problem with the FizzBuzz Problem, Gayle Laakmann McDowell discussed the cognitive trap that a high performer can fall into when faced with a deceptively inelegant real world problem. She called it the “Smart Person’s Mirage.”

I was caught in this trap in an interview once myself. I was asked to write a function that took an integer parameter and wrote out an ascii art diamond pattern in the given size, like this:

  *  
 *** 
*****
 *** 
  *  

The symmetry cried out to me. There must be some way, I was sure, to leverage it in both directions at once. It seemed like an affront to the concept of elegance if I didn’t! Also, there were so many options. Use a buffer, or several, or none? Make nested loops? How should an even parameter be handled?

In a matter of minutes, I had pieces of at least three conflicting solutions scattered all over the whiteboard. That was when the interviewer let me know that that was exactly what the question was designed to test for.

The way out of this failure mode is to not try too hard. As tempting as it is to write code poetry and dazzle my interviewer, the bottom line is that it’s not required. Moving forward with something, anything, that actually completes the task is. Optimizaton, neatness and elegance (and sometimes insight) can come later.

Inspired by Gayle’s post, I revisited the ascii art diamond problem. By letting go of finding a “perfect” solution, I came up with a workable one in a couple of minutes. Here it is in all its kludgy, messy glory.

    /* diamond.c
     * utility to take an integer on the command line and
     * output an ascii art diamond in that size made of splats
     */
    
    #include <stdio.h>
    #include <string.h>
    #include <stdarg.h>
    
    #define MAX_DIMENSION 41
    #define DEFAULT_DIMENSION 5
    
    int main (int argc, char **argv) {
    	char line[MAX_DIMENSION];
	static char on_char = '*';
	static char off_char = ' ';
	int dimension = DEFAULT_DIMENSION;
	int midpoint;
	int lineno = 1;
	int columnno = 1;
	int i, j;

	if (argc > 1) {
		dimension = atoi (argv[1]);
		/* even dimensions end up being treated as the
		 * next highest odd dimension (ex. 4 makes a 5x5)
		 */
    /*		if (dimension % 2 == 0)
			dimension += 1; */
		if (dimension > MAX_DIMENSION)
			dimension = DEFAULT_DIMENSION;
	}

	memset (line, off_char, MAX_DIMENSION * sizeof (char));
	midpoint = dimension / 2 + 1;

	/* print top half of lines to midline */
	for (i = 0; i < midpoint; i++) {
		printf ("%s\n", line);
		line[midpoint - i] = on_char;
		line[midpoint + i] = on_char;
	}
		
	/* print bottom lines */
	for (; i > -1; i--) {
		line[midpoint - i] = off_char;
		line[midpoint + i] = off_char;
		printf ("%s\n", line);
	}
    		
	return 0;
    }

Pointers are just variables (and how I figured this out)

After submitting a handful of coding style fixes to the OPW kernel mentors, it was time to try some real code. The driver file I’ve been working on has a function that’s over five hundred lines long, with a tangle of nested conditionals that cause some of the lines to be indented way too far. The logical thing to do was to extract some of the switch cases into their own functions.

To do it correctly, though, I needed to send a bunch of automatic variables along with the extracted lines as parameters. This included pointers. I assumed it would be easy; but my experience is mainly with pointers to structs, and when I looked at my parameter list, I was honest with myself about how fudged it looked. Plus, I didn’t have the benefit of being lazy and letting my tools do my thinking for me. I don’t own this device, so I have to fully understand every character of what goes into my patch. Without testing it.

As I walked through the extracted lines, I realized I was assuming that since pointer variables are pointers, their referencing is already taken care of, and they can be passed around with impunity. But, since that was an assumption, was it indeed correct? And even if it was, did it give me a crystal clear understanding of my pointer variables’ states? Having defined my zone of uncertainty, I could create a test program. So I ran this:


      1 #include 
      2 
      3 void foo(int *ptr)
      4 {
      5     printf("content of ptr %p\n", ptr);
      6     printf("address of ptr %p\n", &ptr);
      7     ptr = ptr + 4;
      8     printf("content of ptr %p\n", ptr);
      9     printf("address of ptr %p\n", &ptr);
     10     putchar('\n');
     11     
     12     }
     13 
     14 int main()
     15 {
     16     int a = 2;
     17     int *pa = &a;
     18     
     19     printf("content of pa %p\n", pa);
     20     printf("address of pa %p\n", &pa);
     21     putchar('\n');
     22     
     23     foo(pa);
     24     
     25     printf("content of pa %p\n", pa);
     26     printf("address of pa %p\n", &pa);
     27     
     28     return 0;
     29     }

…and got this output:


content of pa 0xbfe65f58
address of pa 0xbfe65f5c

content of ptr 0xbfe65f58
address of ptr 0xbfe65f40
content of ptr 0xbfe65f68
address of ptr 0xbfe65f40

content of pa 0xbfe65f58
address of pa 0xbfe65f5c

Not only was the pointer value unchanged by the function, which was scary proof that I was wrong, the pointer’s address was different inside the function. What? Was the pointer being passed by value like…a plain old automatic variable?

The always helpful folks at Stack Overflow confirmed my suspicions. That’s exactly what it is, and it should be handled like one.


      1 #include 
      2 
      3 void foo(int **ptr)
      4 {
      5     printf("content of ptr %p\n", *ptr);
      6     printf("address of ptr %p\n", ptr);
      7     *ptr = *ptr + 4;
      8     printf("content of ptr %p\n", *ptr);
      9     printf("address of ptr %p\n", ptr);
     10     putchar('\n');
     11     
     12     }
     13 
     14 int main()
     15 {
     16     int a = 2;
     17     int *pa = &a;
     18     
     19     printf("content of pa %p\n", pa);
     20     printf("address of pa %p\n", &pa);
     21     putchar('\n');
     22     
     23     foo(&pa);
     24     
     25     printf("content of pa %p\n", pa);
     26     printf("address of pa %p\n", &pa);
     27     
     28     return 0;
     29     }

content of pa 0xbfef91d8
address of pa 0xbfef91dc

content of ptr 0xbfef91d8
address of ptr 0xbfef91dc
content of ptr 0xbfef91e8
address of ptr 0xbfef91dc

content of pa 0xbfef91e8
address of pa 0xbfef91dc