Thread: Pointers, arithmetic, and order of operation.

  1. #1
    Widdle Coding Peon Aerie's Avatar
    Join Date
    Dec 2004
    Posts
    115

    Pointers, arithmetic, and order of operation.

    I'm mucking around with pointers, trying to get more comfortable with using them in weird and obscure ways(or, more likely, being able to understand others' obscure uses of them), and I came across a situation I want explained to me.

    I've written a little program which wants either 0 or 1 arguments. It outputs some text horizontally by default, but can be switched to vertically with a -v option. It prints a help message if it gets the -h switch. It also prints an error message if it gets too many arguments, or a different message if it gets an invalid argument.

    I deliberately wrote some code to check each condition differently. Error checking is pretty minimal(doesn't even check to see if options are prepended with a dash, it just checks the second element of the argv string in question, so progname hh should work just as well as progname -h):

    Code:
    	if(*++argv == NULL)
    		i = HOR;
    	else if(argc > 2)
    		helpmsg(2);
    	else if((*argv)[1] == 'h')
    		helpmsg(0);
    	else if(*++*argv == 'v')
    		i = VERT;
    	else
    		helpmsg(1);
    All of this code works perfectly when I compile it.

    What confuses me is the EXACT way some of these articles of code work. For example,
    Code:
    	else if(*++*argv == 'v')
    is identical in function to
    Code:
    else if((*argv)[1] == 'v')
    and yet I can't really put my finger on _why_ it works the way it does, except that I would find the latter much easier to read if I understood what it was doing, exactly.

    I know what, in a gross sense, is going on at every line. Since argv is a pointer pointing to a group of pointers, incrementing it by one sets it to the first pointer which would point to an actual program option, so the first line increments the first pointer, dereferences it, and checks the value of the pointer it points to against NULL.

    The third line just checks argv to see if it's too big, then throws a "too many options" error if it is.

    The fifth line derefences the first pointer, again, then treats the set of pointers pointed to by the first pointer like an array, and then accesses the second element of the character array pointed to by the second pointer. If that element is 'h,' it prints the standard help message.

    The seventh line dereferences the first pointer, then increments the second pointer, then dereferences it as well, thus ALSO accessing the exact same character as is accessed by the fifth line.

    Even if my explanation of all these lines is poor, I've demonstrated them to work by running the program. What is annoying me is that I can't "see" how each aspect of pointer incrimentation works.

    *++*argv is a set of four items: *, ++, *, and argv. What element denotes the second pointer, which is incremented by ++?

    I hope this question makes sense... I just need to understand this better for my own peace of mind.
    Last edited by Aerie; 04-19-2005 at 06:57 AM.
    I live in a giant bucket.

  2. #2
    Senior Member joshdick's Avatar
    Join Date
    Nov 2002
    Location
    Phildelphia, PA
    Posts
    1,146
    Operator precedence and associativity: http://msdn.microsoft.com/library/de....operators.asp

    Coding *++*argv might be nice for an obfuscation contest, but you shouldn't get in the habit of writing code no one else can understand. Aim instead to write code that is clear.

  3. #3
    Widdle Coding Peon Aerie's Avatar
    Join Date
    Dec 2004
    Posts
    115
    I thought that postfix increment or decrement were both really low priority; according to that chart, they're really high priority.

    Am I confused about something here?

    I guess my question is: in this instance, is ++ being interpreted as a prefix or postfix operator?

    Also: I wasn't planning on using crap like this everyday in my code. This is deliberately mangled code for learning purposes.

    By the way, the program is actually a really overdone solution to exercise 1-13 in K&R C, second edition. I was going to write a barebones solution(I'm going back through the book and doing EVERY exercise to train my hands to type code more comfortably), but I got bored halfway through and started adding stuff... you know, features'n'stuff.

    Here's the end product:

    Code:
    #include <stdio.h>
    #include <ctype.h>
    
    #define MAXCOUNT 1500
    #define MAXLEN 17
    #define HOR 0
    #define VERT 1
    
    void countinput(void);
    void helpmsg(int);
    void printhor(void);
    void printvert(void);
    
    void helpmsg(int i)
    {
    	if(i == 0) {
    		printf("Program produces histogram of word lengths in input.\n");
    		printf("Max word length is %d.\n", MAXLEN);
    		printf("Input anything, hit EOF control sequence to impliment counting.\n");
    		printf("Defaults to horizontal output, -v will force vertical output.\n");
    		printf("Options:\n");
    		printf("-v		Output vertical graph.\n");
    		printf("-h		Print this message.\n");
    		exit(0);
    	}
    
    	else if(i == 1) {
    		printf("Invalid option. Try -h for help.\n");
    		exit(0);
    	}
    
    	else if(i == 2) {
    		printf("Too many options. Try -h for help.\n");
    		exit(0);
    	}
    
    	else
    		printf("helpmsg: invalid argument.\n");
    }
    
    int charcnt[MAXLEN];
    
    int main(int argc, char *argv[])
    {
    	int i, scratch;
    	for(i = 0; i < MAXLEN; i++)
    		charcnt[i] = 0;
    
    	if(*++argv == NULL)
    		i = HOR;
    	else if(argc > 2)
    		helpmsg(2);
    	else if((*argv)[1] == 'h')
    		helpmsg(0);
    	else if(*++*argv == 'v')
    		i = VERT;
    	else
    		helpmsg(1);
    
    	countinput();
    
    	for(scratch = 0; scratch < MAXLEN; scratch++)
    		printf("%d", charcnt[scratch]);
    	putchar('\n');
    
    	if(i == HOR)
    		printhor();
    	else if(i == VERT)
    		printvert();
    	else {
    		printf("main: panic! i inequal to HOR or VERT!\n");
    		return 1;
    	}
    	return 0;
    }
    
    void countinput(void)
    {
    	int i, c;
    
    	i = 0;
    	printf("Currently, all input(prior to hitting the EOF control sequence) must ");
    	printf("end with a space, carriage return, or other non-alphanumeric character.\n");
    	printf("We apologize for this inconvenience.\n");
    	while((c = getchar()) != EOF) {
    		if(c == ' ' || c == '\t') {
    			charcnt[0]++;
    			if(i > 0) {
    				if(charcnt[i] < (MAXCOUNT - 1))
    					charcnt[i]++;
    				i = 0;
    			}
    		}
    		else if(c == '\n') {
    			if(i > 0) {
    				if(charcnt[i] < (MAXCOUNT - 1))
    					charcnt[i]++;
    				i = 0;
    			}
    		}
    		else if(isalnum(c))
    			i++;
    		else {
    			if(i > 0) {
    				if(charcnt[i] < (MAXCOUNT - 1))
    					charcnt[i]++;
    				i = 0;
    			}
    		}
    	}
    }
    
    
    void printhor(void)
    {
    	int i;
    	printf("Whtspc:");
    	for(i = 0; i < charcnt[0]; i++)
    		putchar('*');
    
    	printf("\nWordlen\n");
    
    	for(i = 1; i < MAXLEN; i++) {
    		printf("\n      %d", i);
    		for(; charcnt[i] > 0; charcnt[i]--)
    			putchar('*');
    	}
    	printf("\n");
    }
    
    void printvert(void)
    {
    	int i, j, k = 1;
    	printf("Whtspce");
    	for(i = 1; i < MAXLEN; i++)
    		printf("%7d", i);
    
    	putchar('\n');
    
    	i = 0;
    
    	while(i < MAXCOUNT && k < MAXLEN) {
    		printf("\n   ");
    		i++;
    		k = 0;
    		j = 0;
    		while(j < MAXLEN) {
    			if(charcnt[j] > 0) {
    				printf("   *   ");
    				charcnt[j]--;
    			}
    			else {
    				printf("       ");
    				k++;
    			}
    		j++;
    		}
    	}
    	putchar('\n');
    }
    Last edited by Aerie; 04-19-2005 at 07:06 AM.
    I live in a giant bucket.

  4. #4
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    If we follow the chart that Mr Dick helpfully linked to, we see that the pointer indirection and prefix increment operators have the same priority and evaluate from right to left while the equality operator has a lower precedence. With this knowledge we can deconstuct the statement:
    Code:
    char ** argv;
    if(*++*argv == 'v')
    breaks down to:
    Code:
    char* temp1 = *argv;  // if(*++*argv == 'v')
    ++temp1;              // if(*++*argv == 'v')
    char temp2 = *temp1;  // if(*++*argv == 'v')
    if (temp2 == 'v')     // if(*++*argv == 'v')

  5. #5
    Widdle Coding Peon Aerie's Avatar
    Join Date
    Dec 2004
    Posts
    115
    And according to self-same chart, postfix increment operators are higher priority, which confused me. However, since you've outlined this and indicate that ++ is in this case acting as a prefix operator, I'll assume that particular aspect of the chart is wrong.

    So in *++*argv, working from right to left, argv is bound to by *, dereferencing the first pointer. ++ binds to that, thus incrementing the second pointer. * dereferences the incremented pointer, thus resolving to 'v'.

    Logically that's what's happening, I mean, even if the underlying mechanics are a bit more complex.

    I want to make absolutely sure I know this stuff as well as the back of my hand, because I know I'm going to encounter it, or possibly need to use it at some point, and I want to be prepared.

    Besides, everyone and his brother tells me that to really be an outstanding C programmer, you have to be a wizard with pointers... which is exactly what I'd like to become.
    I live in a giant bucket.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM
  2. arithmetic calculation ...again
    By wombat in forum C Programming
    Replies: 11
    Last Post: 09-11-2002, 06:16 AM