Thread: String incrementing

  1. #1
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174

    String incrementing

    I have this function that is supposed to take in a string and move the last character to the front of the string.

    Code:
    char *rotateRight(char *string) {
        char *cur = NULL;
        
        for (cur = string; *(cur++) != '\0'; cur++) {
            printf("*cur = %c\n", *cur);
            printf("cur = %s\n", cur);
        }
        *string = *cur;
        printf("%s\n", string);
        
        return string;
    }
    But in my terminal when I enter the string abcdef I get the output

    *cur = b
    cur = bcdef
    *cur = d
    cur = def
    *cur = f
    cur = f
    Sbcdef
    So it seems to be skipping each letter in between (I thought that string++ would incrememt by only each character),

  2. #2
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174
    Oh hang on, I've realized that my for loop breaker *(cur++) != NULL is incrementing my cur string. Hmm... does that mean if I had used an integer such as i in my for loop, then if my for loop were to be

    Code:
    for(i=0; i++ != 10; i++)
    then my i would be incremented twice in each loop I guess. But of course in this case I could just change the breaking condition to i<10, but how can I do a similar thing with the string? I want to break the loop on the character before the null terminator.

  3. #3
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Nowhere is your code doing "string++". You are incrementing pointers, not strings. They are different things.

    If you look at your for statement, it is incrementing cur twice around every loop.

    Coming into the loop, the first thing that will happen is that the test "*(cur++) != '\0" will be done. *cur is 'a' (which is not '\0'). cur is then incremented, so *cur is 'b' (hence the first two lines of output involving *cur = b). cur is then incremented (as per the bit at the end of the for() statement, so *cur is 'c'. Then the test "*(cur++) != '\0" will be done. *cur is 'c' (which is not '\0'). cur is then incremented, so *cur is 'd' (hence the first two lines of output involving *cur = d).

    What you need to do is remove the increment operator from the test.

    Even then, the code won't do what you want since, when the loop finishes, *cur will be pointing at the zero terminator (one after the 'f'). I'll leave fixing that as an exercise.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  4. #4
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174
    Quote Originally Posted by grumpy View Post
    Nowhere is your code doing "string++". You are incrementing pointers, not strings. They are different things.

    If you look at your for statement, it is incrementing cur twice around every loop.

    Coming into the loop, the first thing that will happen is that the test "*(cur++) != '\0" will be done. *cur is 'a' (which is not '\0'). cur is then incremented, so *cur is 'b' (hence the first two lines of output involving *cur = b). cur is then incremented (as per the bit at the end of the for() statement, so *cur is 'c'. Then the test "*(cur++) != '\0" will be done. *cur is 'c' (which is not '\0'). cur is then incremented, so *cur is 'd' (hence the first two lines of output involving *cur = d).

    What you need to do is remove the increment operator from the test.

    Even then, the code won't do what you want since, when the loop finishes, *cur will be pointing at the zero terminator (one after the 'f'). I'll leave fixing that as an exercise.
    Thankyou for that explanation. I have tried to fix it by working with the array of (EDIT: characters, not strings) instead. What I've come up with is

    Code:
        
    int i = 0;
    for (cur = string; cur[i+1] != '\0'; cur[i++]) {
        cur[i] = string[i+1];
    }
        
    string[0] = *cur;
    Which is getting an error

    rotate.c:50:45: error: value computed is not used
    So something isn't right with the cur[i++]
    Last edited by Mentallic; 09-02-2012 at 01:05 AM.

  5. #5
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174
    I've tried a few things now, and I've realized that when I set cur = string, they're pointing to the same place in memory so as I make changes to one, I'm also making changes to the other.
    So I've tried to use strcpy, but I'm also confused about that. From the site STRCPY - copy one string to another. it says how to use strcpy which is

    Usage:

    #include <string.h>
    ptr = strcpy( s1, s2 );

    Where:

    char *s1;
    points to an area of memory that is to receive the copied characters.
    const char *s2;
    points to the string from which characters will be copied. This must end with the usual '\0'.
    char *ptr;
    points to the copied string (i.e. "s1").
    And after trying to implement that (I'm unsure with what to do with the pointer ptr, so to skip the warning that I'm not using it I made a print statement), I now have


    Code:
    char *rotateRight(char *string) {
        char *cur = NULL;
        char *ptr = NULL;
        int i = 0;
        
        ptr = strcpy(cur, string);
        
        printf("ptr = %p\n", ptr);
        
        for (i=0; cur[i+1] != '\0'; i++) {
            printf("cur[%d] = %c\n", i, cur[i]);
            string[i+1] = cur[i];
        }
        *string = cur[i];
        printf("string = %s\n", string);
        
        return string;
    }
    Which just seg faults. I feel pretty hopeless for having struggled this much with such a seemingly simple program...

  6. #6
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    This is one simple way to copy a string manually (not using strcpy()).
    Code:
    Above main:
    
    #include <stdio.h>
    
    void strcopyManually(char *, char *);
    
    
     In main():
    char str[]= {"Hello"};
    char t[] = {"Goodbye"};
    
       strcopyManually(str, t);
       printf("str: %s,    t: %s \n",str,t);
    
       return 0;
    }
    void strcopyManually(char *s, char *t) {  //using indices only, instead of pointers
       int i, j;
       i=j=0;   
       while((s[i++]=t[j++]) != 0)
          ;  //yes, this is a one line while loop
       s[i]=0;  //add the end of string char to the array. NOW it's a string again.
    
    }
    if string is an array, there is no need to return anything from creating a copy. Of course, it looks almost the same way if you use pointers.

    By "rotate right" -- is that a shift right or something else?

  7. #7
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174
    I don't quite understand how that function works. What is
    Code:
    (s[i++]=t[j++]) != 0
    doing exactly?

    Also, I want to understand why strcpy isn't working for me in my case, because if I can't understand why it's not working, then I doubt there's any hope in me creating my own working strcpy function.

    By right rotate I mean to move the last-most character to the front of the word, and then have every letter after it incremented up a spot. So for example,

    "abcdef" should become "fabcde" and left rotate would be the opposite way around: "bcdefa".

  8. #8
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    First, the assignment (copying) is made, from t to s array, and after each char has been copied, the index for each array, is then incremented.

    Note that s[++i] = t[++j] or any other incrementing pattern, would not work perfectly (chars would be skipped).

    Oh, OK. By rotate, you mean a real rotate, and either right or left. The thing to keep in mind is that you have to have the order down right - otherwise you can rotate your array perfectly, but have it turn out badly, because some parts of it, somewhere in the rotation, have overwritten some other parts. So order of movement is critical.

    I'm sure there are lots of ways to think about it, but does this make sense, for a right rotate, logic:

    1) put the 'f' in abcdef, into a temp char variable.
    2) move the e into the f's former place
    3) move the d into the e's former place
    4) move the c into the d's former place
    5) move the b into the c's former place
    6) move the a into the b's former place
    and last, put the temp char's f, into the a's former spot.

    That makes sense?

    So we have 3 steps:
    1) get the last char - f into a temp char
    2) shift all the remaining chars, starting from the right most end, one space to the right
    3) put the temp char from #1, into the left most spot in the array.

    Which means that first off, you have to know where the last char is, so:

    int len = strlen(charArrayName) -1; //see note below for why -1

    Then the #1, the #2 (a for loop), and finally #3, the final assignment.

    Try that, and use JUST THE INDICES, instead of pointers. Pointers give students LOTS of trouble, and trying to figure out what they were trying to do is just wicked!

    Any questions, fire away. Remember that you will be decrementing the index in the for loop, NOT incrementing it upward, like usual.

    P.S. strlen() returns a number one greater than the index number -- I mean if the array is s[]={"a"}, strlen(s]) will return 1, whereas the index for 'a' is 0.

    So, to fix it, subtract one from the strlen() number:
    Code:
    int len = strlen(arrayName) -1;
    Last edited by Adak; 09-02-2012 at 04:23 AM.

  9. #9
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    I would recommend solving this problem by starting at the very basics.

    First, a string in C is just a character array. Assume you have string ABCDE described by variable str. I say described, because str may be a character array, or a character pointer; in both cases the following description stays exactly the same. Thus, str[0] == 'A', str[1] == 'B', and so on up until str[4] == 'E' and str[5] == '\0', since an ASCII NUL (\0) terminates strings in C. (Also therefore a five-character string needs six characters of storage, at indexes 0, 1, 2, 3, 4, 5.)

    To "rotate" the string in such a way that the first character moves to the second position, second to the third, and so on, with the final character moved back to the first position, we need a loop. It helps if you doodle on gridded paper to visualize this:
    Code:
    Before:  A  B  C  D  E  \0
    After:   E  A  B  C  D  \0
    Index:   0  1  2  3  4  5
    In effect, we want to do str[1] = str[0], str[2] = str[1], and so on, until str[length-1] = str[length-2], and str[0] = str[length-1]. However, we cannot just do that in a loop in that order, because we'd be overwriting the data we need in the next assignment at each iteration! The result would be just the first character duplicated over the entire string. (Yes, I've done it myself too. On many separate occasions.)

    Note that the other "rotation" is trivial, because the indices go the other way: str[0] = str[1], str[1] = str[2], and so on, until str[length-2] = str[length-1], and str[length-1] = str[0]. All you need to do is save the initial character, do a loop with one less iteration than characters in the string, and plug back the saved character at the end of the string. This "rotation" is not that useful as an exercise, because most learners never notice this detail. In the other one this detail will bite you in the ass, unless you're careful. (It's not just for "learners", either: situations similar to this often breed buffer overrun and off-by-one bugs. So it is a good thing to get it right, starting at the basics.)

    So, now we know exactly what we need to do.

    There are two basic approaches I call forward-delay and reverse order. Forward-delay does the rotation loop in increasing indexes, reverse order in decreasing indexes -- starting at the last character and working backwards to the start of the string. If you consider the case for the other "rotation", and how its indexes are perfectly ordered for a normal straight loop, using a reverse loop should be an obvious choice. All you need to do is save the initial character for later, then copy the characters starting at the end of the string, and working back towards the start. This is the preferred solution.

    In some special but related cases, the "string" is actually composed of variable-length structures (something like PNG, JPEG, TIFF or MPEG streams, for example) which really cannot be scanned backwards at all. The forward-delay is perfect for those. The idea is to cache each overwritten value as you go along, keeping it cached only long enough to use it at the next iteration. It's very much like pushing a fold along a heavy rug, with the fold as the cache.

    Because the forward-delay method is not something you should ever use with strings, I'll include the string implementation here. The following function will move the last character (byte) in the string to the first position, moving all others one place down (right, if you read left-to-right):
    Code:
    void rotate_right(char *const str)
    {
        size_t  i;      /* Index in string */
        char    s, c;  /* saved character, temporary character */
    
        /* We cannot do anything to NULL or empty strings. */
        if (!str || !*str)
            return;
    
        /* Save the initial character. */
        s = str[0];
    
        /* Move each successive character one place down,
           rotating through the saved character s.
           We need the temporary character c for that swap. */
        for (i = 1; str[i]; i++) {
            c = s;
            s = str[i];
            str[i] = c;
        }
    
        /* The saved character s will now contain the final,
           non-NUL character in the string. Move it to its
           correct position at the start of the string. */
        str[0] = s;
    }
    Note that the above function will not work for multibyte character sets, in particular the UTF-8 character set, because each character may take one to six indexes (although currently only one to three are used). There is a very simple solution for that, related to memmove() and the length of the final character in bytes; can you find out for yourself what it is?

  10. #10
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174
    Both of you, thankyou for your thorough responses. I tried to write out the program in a way that made most sense to me, and I came up with

    Code:
    char *rotateRight(char *string) {
        int len = strlen(string)-1;
        char tmp = '\0';
        int i = 0;
        
        tmp = string[len];
        for (i=len-1; i>=0; i--) {
            string[i+1] = string[i];
        }
        string[0] = tmp;
        printf("string = %s\n", string);
        
        return string;
    }
    Which works, but at first I had a char *tmp variable instead, and I instead had the (relevant) lines

    Code:
        char *tmp = NULL;
        
        *tmp = string[len];
    
        string[0] = *tmp;
    Which was seg faulting. Why is this? Both variations seem equivalent to me so I can't see why the pointer didn't work while the char did.

  11. #11
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    It was crashing because temp still had the address of NULL, and you can't assign a value to that memory address.
    Last edited by Adak; 09-04-2012 at 06:43 AM.

  12. #12
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174
    string(len) isn't beyond the string because I defined len as
    Code:
    int len = strlen(string) - 1;
    and it works when I use char tmp as opposed to char *tmp, so I still cannot understand why it's working with the char but not the pointer.

    But I'll add in some lines of code as you suggested to see if I can figure it out

  13. #13
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    Yes, I corrected that reply. Re-read it.

  14. #14
    Registered User
    Join Date
    Mar 2010
    Location
    Australia
    Posts
    174
    Ahh I see now! Thanks Adak!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Pointer Incrementing
    By zoowho in forum C Programming
    Replies: 4
    Last Post: 02-25-2010, 12:42 PM
  2. Incrementing Twice?
    By Kemaratu in forum C Programming
    Replies: 3
    Last Post: 09-27-2009, 11:13 PM
  3. incrementing problems...
    By MikeyIckey in forum C Programming
    Replies: 6
    Last Post: 09-23-2007, 04:40 PM
  4. Incrementing by 2 not 1
    By Eckey in forum C++ Programming
    Replies: 6
    Last Post: 10-14-2004, 03:16 PM
  5. Incrementing variables
    By Bazz in forum C++ Programming
    Replies: 4
    Last Post: 11-08-2001, 09:55 AM