Thread: Pass-by-value weirdness

  1. #1
    Registered User
    Join Date
    Feb 2009
    Posts
    2

    Pass-by-value weirdness

    I'm still trying to get my head completely around pointers, and so I've been writing a few programs to see what does and doesn't work. This code is to find anagrams. It takes in a one word argument, and checks it against a list of words (words.txt, a sample of which I have attched. The real file is too big to attach here).

    If you run the following code
    Code:
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    
    int isAnagram(char word1[], char word2[]);
    void removeFirst(char letter, char* string[]);
    void stripWord(char* junk[]);
    
    static const int yes = -1;
    static const int no = 0;
    
    int main(int argc, char* argv[]) {
    	if ( argc != 2 ) {
    		printf("Please enter exactly one argument\n");
    		return -1;
    	} else if ( strlen(argv[1]) > 20 ) {
    		printf("Please enter an argument that is at most 20 characters\n");
    		return -1;
    	}
    
    	char word[20];
    
    	strcpy(word, argv[1]);
    	
    	static const char filename[] = "words.txt";
    	FILE *file = fopen ( filename, "r" );
    
    	char line [ 128 ];
    	char tmp[128];
    	while ( fgets ( line, sizeof line, file ) != NULL ) {
    	
    		int i;
    
    		for ( i = 0; i < 256; i++ )
    		{
    			if ( line[i] == '\n' )
    			{
    				line[i] = '\0';
    				break;
    			}
    		}
    	
    	
    		//sprintf(tmp,"%s",word);
    		printf("%s\n",word);
    		if ( isAnagram(line,word) == yes ) printf("%s\n", line);
    		printf("%s\n",word);
    	}
    	
    	fclose ( file );
    
    	return 0;
    }
    
    int isAnagram(char word1[], char word2[]) {
    
    	if ( strlen(word1) != strlen(word2) ) return no;
    	if ( strcmp(word1,word2) == 0 ) return no;
    	
    	int i;
    	for (i=0; i<strlen(word1);i++) {
    		if ( strchr(word2,word1[i]) == NULL ) return no;
    		else {
    			removeFirst(word1[i],&word2);
    		}
    	}
    	
    	return yes;
    }
    
    void removeFirst(char letter, char* string[]) {
    	//remove the first occurrence of 'letter' in 'word'
    	
    	char temp[20]="";
    	int i,index;
    	
    	index = strchr(*string,letter)-*string;
    	
    	for (i=0; i<strlen(*string);i++) {
    		if ( i != index ) sprintf(temp,"%s%c",temp,(*string)[i]);
    	}
    	strcpy(*string,temp);
    }

    The problem I'm having is at these lines
    Code:
    //sprintf(tmp,"%s",word);
    printf("%s\n",word);
    if ( isAnagram(line,word) == yes ) printf("%s\n", line);
    printf("%s\n",word);
    When it runs like that, the output will show you the value of the variable 'word', before and after the execution of the isAnagram function. This value appears to change as the program runs.

    However, if you uncomment the sprintf line, and pass the value tmp instead of word, like so
    Code:
    sprintf(tmp,"%s",word);
    printf("%s\n",word);
    if ( isAnagram(line,tmp) == yes ) printf("%s\n", line);
    printf("%s\n",word);
    the value of word doesn't change at all, and it works fine.

    So, my question is, Why is 'word' being changed by isAnagram when I didn't pass it's address to the function?

    Thank you

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by sillyfofilly View Post
    So, my question is, Why is 'word' being changed by isAnagram when I didn't pass it's address to the function?

    Thank you
    Perhaps we understand "didn't" differently, because I would say that you did so:
    Code:
    if ( isAnagram(line,word) == yes ) printf("%s\n", line);
    You need to make a local copy in the function if you don't want changes to affect the original variable -- or send a copy to the function, as w/ "tmp".

    You can't pass an array by value.
    Last edited by MK27; 02-16-2009 at 07:31 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    Registered User
    Join Date
    Feb 2009
    Posts
    2
    Ok, so passing it like this
    Code:
    if ( isAnagram(line,word) == yes ) printf("%s\n", line);
    allows for the value to be changed? I thought that automatically created a local copy of the variable, and to specifically allow it to be changed you had to pass the address like this
    Code:
    if ( isAnagram(line,&word) == yes ) printf("%s\n", line);

  4. #4
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    word pointer is not changed, buffer pointed by the word pointer could be changed
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  5. #5
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    All arrays in C are passed "by reference" (or put another way, as the address of the first element). &word would be a pointer to pointer to char.

    It also means that your removefirst doesn't need to be char *string[].
    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by sillyfofilly View Post
    Ok, so passing it like this
    Code:
    if ( isAnagram(line,word) == yes ) printf("%s\n", line);
    allows for the value to be changed? I thought that automatically created a local copy of the variable, and to specifically allow it to be changed you had to pass the address like this
    Code:
    if ( isAnagram(line,&word) == yes ) printf("%s\n", line);
    No. The only reason you got away with the second one is because of this:
    Code:
    void removeFirst(char letter, char* string[])
    which is very wrong, and the only reason that works is because you are working on the first element. I think if you try and do something to the second element here, you will notice it's the forth letter. That's because char* string[] is an array of pointers, not an array of characters. If you try this, for example
    Code:
    if ( isAnagram(line,&word) == yes ) printf("%s\n", line);
    You will get an error.
    So to make it clear again: You can't pass an array by value in C, and a string is an array of characters.
    Last edited by MK27; 02-16-2009 at 08:00 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Complete Beginner
    Join Date
    Feb 2009
    Posts
    312
    So to make it clear again: You can't pass an array by value in C
    And to make it confusing again: C always uses call-by-value.

    If you pass a pointer-to-array to a function, you are calling the function "by value". But it's the pointer's value that is passed, not the value of the array. Consider the following code:

    Code:
    #include <stdio.h>
    
    void foo(int *bar)
    {
            bar = 3;
    }
    
    int main()
    {
            int arr[1024];
            int *p;
    
            p = arr;
    
            printf("%p\n", p);
            foo(p);
            printf("%p\n", p);
    
            return 0;
    }
    This will print the same value of p twice, because foo() only modifies its local copy of p, namely bar. The array values at arr will remain unchanged. On the other hand, if foo() would instead be "*bar = 3;", this would actually change the first value of arr in main().

    Maybe it helps to think of pointers as notes with a number written on them. If you pass such a note to a function, you're actually making an exact copy of the note (call-by-value). The function is free to do anything with it, including overwriting the address on the note. The calling function won't be able to tell (because it has its own note), and of course this won't change the stuff at the specified address. On the other hand, going to the specified address and changing values there will of course have the same effect in the calling function.

    Hope this helps.

    Greets,
    Philip

  8. #8
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by matsp View Post
    &word would be a pointer to pointer to char.
    In main it wouldn't be. It would be a pointer to an array of chars. So you wouldn't be able to call removeFirst from main.

    In isAnagram, word is a char pointer, so it works.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Snafuist View Post
    Maybe it helps to think of pointers as notes with a number written on them. If you pass such a note to a function, you're actually making an exact copy of the note (call-by-value). The function is free to do anything with it, including overwriting the address on the note. The calling function won't be able to tell (because it has its own note), and of course this won't change the stuff at the specified address. On the other hand, going to the specified address and changing values there will of course have the same effect in the calling function.
    What that means is if you reassign the pointer in the called function:
    Code:
    void somefunc(char *string) {
             char new[]="this";
             string=new;
    }
    This will not have any affect outside, because the local copy of the "pointer value" (an address) has changed, so operations on string no longer affect the variable at the address string originally referred to. Also, string won't contain the array it originally did. But if you don't reassign the pointer, then changes to string are global.

    By the way, these mean the same thing:
    Code:
    void somefunc(char *string) {
    void somefunc(char string[]) {
    which is different from
    Code:
    void somefunc(char* string[]) {
    Also, you may want to try Snafuist's code as this demonstrates the use of printf's handy %p, which will probably help you to get more of a grip on the pointer concept.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Speed test result
    By audinue in forum C Programming
    Replies: 4
    Last Post: 07-07-2008, 05:18 AM
  2. Replies: 3
    Last Post: 11-22-2007, 12:58 AM
  3. Pass by reference
    By mcgeady in forum C Programming
    Replies: 11
    Last Post: 02-17-2005, 03:01 AM
  4. pass be reference versus pass by value
    By Unregistered in forum C++ Programming
    Replies: 2
    Last Post: 08-01-2002, 01:03 PM
  5. Replies: 3
    Last Post: 04-02-2002, 01:39 PM