Resolved Print out a string in reverse in C

Discussion in 'Mac Programming' started by phySi0, Aug 13, 2011.

  1. phySi0, Aug 13, 2011
    Last edited: Aug 13, 2011

    phySi0 macrumors member

    Joined:
    Jun 19, 2011
    Location:
    You don't want to know!
    #1
    Hi guys, I think this is my first post here. I am looking for a solution to a problem to a programming problem that I've spent ages on, but can't seem to fix. I've searched for other threads, but none are using the same code as me and they seem to be complicated.

    Okay, so I am a beginner to programming and am using this problem as an exercise. I am basically trying to print out text input by the user, but in reverse. Here is the code:

    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main ()
    {
        char text[50];
        char reverse[50];
        int t;
        int r;
        int null;
        
        printf("Enter some text (max 49 characters): ");
        scanf("%s", text);
        
        r = strlen(text) - 1;
        null = r + 1;
        
    	for (t = 0 ; t <= null ; t = t + 1, r = r - 1)
        {
            text[t] = reverse[r];
        }
        
        printf("%s\n", reverse);
        
        fflush(stdin);
        getchar();
    }
    
    I am using TextMate and compiling using gcc in the Terminal. Whenever I enter the string, it just prints out garbled nonsense of the same length. I've introduced a null variable, which is equal to the length of the string plus 1, so the for loop knows when to stop. Before I used that, I used the r variable for that, then I spotted my mistake. Problem is, the output hasn't changed much, still just junk.

    Bizarrely, when I change the text and reverse character amount from 50 to 51, it just outputs a blank line. This is really confusing, please help. :confused:

    Here's a screenshot of me compiling and running the app, once with the 51 chars in the arrays, the second time with 50.
     

    Attached Files:

  2. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #2
    Quick scan:

    Code:
    
    text[t] = reverse[r];
    
    
    Which string contains valid data and which is the one you are trying to use as a bit-bucket for the reversed string?

    B
     
  3. phySi0 thread starter macrumors member

    Joined:
    Jun 19, 2011
    Location:
    You don't want to know!
    #3

    text is where the text is scanned in when the user inputs some text, reverse is the string that will be printed out at the end.
     
  4. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #4
    I suggest you take a piece of paper, make a drawing of the the array text (50 little boxes), the array reverse (50 little boxes), fill the first six boxes of 'text' with 'H', 'e', 'l', 'l', 'o', and 0 because that is what might be in there after the scanf, and the rest with question marks, and fill the whole of array 'reverse' with question marks because you don't know what's in there. Then do the operations that your code would do. What do you end up with in the arrays 'test' and 'reverse'?

    Is there actually any code in your program that puts _anything_ into the array 'reverse'?
     
  5. phySi0 thread starter macrumors member

    Joined:
    Jun 19, 2011
    Location:
    You don't want to know!
    #5
    Thank you so much! I went over the code so many times, I can't believe I missed out such a tiny thing. I've reversed the location of text and reverse in relation to the equal sign. I now have:
    Code:
     reverse[t] = text[r];
    This seems to have fixed it, but now I see a question mark at the end of the reversed text, is this the null character?
     
  6. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #6
    So what is the line of code I quoted supposed to do? What is on the left what is on the right? Which one pulls it's values from what?

    B
     
  7. phySi0 thread starter macrumors member

    Joined:
    Jun 19, 2011
    Location:
    You don't want to know!
    #7
    It's fixed, gnasher729 helped out. It was supposed to be that reverse takes its text from text, but it was the other way round. That was the problem. I missed it, gnasher helped me fix it. Thanks anyway.
     
  8. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #8
    Why do you think I quoted that line of code and asked what I asked?

    Is there any code that null terminates reverse? What is it's length?

    B
     
  9. phySi0 thread starter macrumors member

    Joined:
    Jun 19, 2011
    Location:
    You don't want to know!
    #9
    Code:
    r = strlen(text) - 1;
        null = r + 1;
    
    r is how many characters are in the array, minus the 1 for the null. null is then r + 1, so that's where the null is. Because in the for loop, r goes down each time to take that character and apply it to reverse, it's volatile. So null is always the same, where the null is, and that's the condition, so it should stop where the null stops. Have I made a mistake?
     
  10. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #10
    Code:
    null = r + 1;
    doesn't put "\0" in the string reverse. It doesn't do anything to the string reverse.

    How might that look?

    You have a number of ways to deal with this and need to work it out for yourself which approach makes sense to you.

    One thing that might help you debug this would be to intiialize the reverse string and print intermediate values within the loop if you don't yet know how to use a debugger.

    B
     
  11. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #11
    Back to the drawing on the sheet of paper. Look at the very first and the very last characters that are copied. Where do you take them from? (I don't have the latest source code, so I don't know if it's the first or the last character that goes wrong).
     
  12. phySi0, Aug 13, 2011
    Last edited: Aug 13, 2011

    phySi0 thread starter macrumors member

    Joined:
    Jun 19, 2011
    Location:
    You don't want to know!
    #12
    I see! Because null is r + 1, that's where the null is in the text, but then the for loop takes the character before 0 as well, because I didn't start at the null character.

    So, null just equals r now, and then I'll just assign a null terminator to the character after the last assigned one.

    Here is the new code:
    Code:
    #include <stdio.h>
    #include <string.h>
    
    int main ()
    {
        char text[50];
        char reverse[50];
        int t;
        int r;
        int null;
        
        printf("Enter some text (max 49 characters): ");
        scanf("%s", text);
        
        r = strlen(text) - 1;
        null = r;
        
    	for (t = 0 ; t <= null ; t = t + 1, r = r - 1)
        {
            reverse[t] = text[r];
        }
    
    	reverse[t] = 0;
    
    //reverseT because the for loop assigned the last char, then added 1 to t right afterwards already, so it's not needed to do that manually.
        
        printf("%s\n", reverse);
        
        fflush(stdin);
        getchar();
    }
    
    Yes, this seems to work! Thanks gnasher and balamw!
     
  13. lloyddean macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #13
    Now that you've got something that works I'll throw out this one:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main ()
    {
        char    string[50];
        int     length;
        
        printf("Enter some text (max 49 characters): ");
        scanf("%s", string);
    
        length = strlen(string);
        while ( length-- )
        {
            printf("%c", string[length]);
        }
    
        printf("\n");
    
        fflush(stdin);
    
        return EXIT_SUCCESS;
    }
    
     
  14. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #14
    phySi0 the strength of lloyddean's approach is that it gets rid of a number of intermediate variables simplifying and clarifying the approach.

    If you goal is really only to print the string in reverse, you don't actually need to store that anywhere. Likewise with your two indices (t,r) and null you really only need one.

    B
     
  15. mrichmon macrumors 6502a

    Joined:
    Jun 17, 2003
    #15
    Explicitly setting the trailing NULL to the "reverse" array as shown in the quoted code works. But this is not good practice.

    A safer way is to explicitly fill the "reverse" array with NULLs before working with that array. You can fill the array with NULLs by putting the line:
    Code:
    bzero(reverse, 50);
    
    after the scanf(...) line.

    By filling the "reverse" string with NULLs then there is no need for the "reverse[t] = 0;" line. The reason why this is better practice is that by filling the destination with NULLs the destination array is NULL terminated through all of the processing.


    Reverse a string is a common introductory programming problem. If you are keen you might want to think about how to simplify your "for" loop condition. There is a solution to this that uses only two integers and a for loop that only increments one integer.

    Code:
    int input_length;    // length of input string
    int i;               // loop counter
    
    If you work out how to do this with just these two integers then the next level of improvement is to eliminate the "reverse" array. (Hint: reverse the string in-place in the "text" string.)
     
  16. phySi0 thread starter macrumors member

    Joined:
    Jun 19, 2011
    Location:
    You don't want to know!
    #16
    Wow! Thanks, this makes much more sense. But what does the EXIT_SUCCESS do?
     
  17. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #17
    Terminates main properly. Same as
    Code:
    return 0;
    which you should have in your code as well ideally. Otherwise make it void main instead of int main.

    B
     
  18. phySi0 thread starter macrumors member

    Joined:
    Jun 19, 2011
    Location:
    You don't want to know!
    #18
    Filling the string with nulls! I would never have thought of that. Lloyddean already fixed it without requiring the reverse array, so I can't really use that as an exercise, but thanks everyone for the help. Some of the solutions here, I would never have thought of them.

    Do you guys have any tips for optimising code and how to write efficient code?

    ----------

    So, in what situation would not having this be bad? What is the purpose of terminating main? Is it just to exit the program when everything is done? If it does that already, why is the void needed?
     
  19. lloyddean macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #19
    There a lots of ways to do this. If you don't mind using pointers then this version will call a subroutine to reverse the string in-place and print it once. This would reduce overall runtime as 'printf' would be called only once.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char* reverse(char string[])
    {
        char*   lhs   = &string[0]-1;
        char*   rhs   = &string[strlen(string)];
        char    temp;
        while ( ++lhs < --rhs )
        {
            temp = *lhs;
            *lhs = *rhs;
            *rhs = temp;
        }
        
        return string;
    }
    
    int main ()
    {
        char    string[50];
        
        printf("Enter some text (max 49 characters): ");
        scanf("%s", string);
    
        fflush(stdin);
    
        printf("%s\n", reverse(string));
    
        return EXIT_SUCCESS;
    }
    

    It's been a looong time but if I recall correctly the C89 standard requires 'main' returns an 'int'. Any non zero value indicates to a shell that an error occurred - but not what error.
     
  20. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #20
    "void main ()" is not valid C.

    The return value of main () makes a difference for command line tools running in a shell, where the shell can examine the return value. For example, a compiler or a program that should copy a file will return 0 if it succeeds and non-zero if it fails, and the shell can then take appropriate action. An example of running in a shell would be when you start "Terminal" and run a program.
     
  21. mrichmon macrumors 6502a

    Joined:
    Jun 17, 2003
    #21
    Traversing the array in reverse and printing each character as shown by lloyddean was my first thought in this thread. Then when I saw the approach that the original poster write I figured they were aiming to reverse a string in memory.

    Plenty of good implementations in this thread. To reverse an array in-place using a for loop you can use:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main ()
    {
        char    string[50];
        int     inputlength;
        int     i;
    
        printf("Enter some text (max 49 characters): ");
        scanf("%s", string);
    
        fflush(stdin);
        inputlength = strlen(string);
    
        for(i=0; i < (inputlength/2); i++) {
            // Swap characters
            char tmp = string[i];
            string[i] = string[inputlength - 1 - i];
            string[inputlength - 1 - i] = tmp;
        }
    
        printf("%s\n", string);
    
        return 0;
    }
    
    A good exercise is to take an in-place string reverse and change the code to reverse all the words in a space separated string. That is, if the input string is "123 4567 67890" the output should be "321 7654 09876".
     
  22. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #22
    It definitely isn't valid C++.

    However the C standard does allow for other return types than int

    http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf

    Seems to allow for
    Code:
    double main (void)
    or any other variant.

    Not recommended, sure, but invalid?

    B
     
  23. lloyddean macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #23
    Thanks for that correction.

    I've been using C++ for so long that I have to look up my C uncertainties - which I was to lazy to do this time and "misremembered" instead.

    Sorry phySi0
     
  24. jpcarro macrumors 6502

    jpcarro

    Joined:
    Mar 13, 2009
    Location:
    On your nine
    #24
    This is a great educational thread. Thanks to the contributors for patiently addressing some programming basics.

    I'm a big proponent of self-documenting code (because I hate documenting) and C++ can get away from self-documentation from line 1. Both mrichmon and lloyddean's methods are good, but have issues for the novice coder.

    In mrichmon's lines:
    Code:
        for(i=0; i < (inputlength/2); i++) {
            ...
        }
    The coder must understand that an inputlength of 10 or 11 divided by 2 is 5. The understanding of the offset from 0 plus integer division rounding is required. This can throw a new programmer.

    As for lloyddean's elegant use of subroutines and pointers:
    Code:
        char*   lhs   = &string[0]-1;
        char*   rhs   = &string[strlen(string)];
        ...
        while ( ++lhs < --rhs )
        {
           ...
        }
    Here, the only niggle is that the pointers "lhs" and "rhs" are set respectively 1 byte prior to and 1 byte past the string's actual start and end locations because the subsequent while loop increments/decrements the pointers before execution. Again, just something that makes novice coders go "huh". Simple fix for clarity purposes would be:
    Code:
        char*   lhs   = &string[0];                  
        char*   rhs   = &string[strlen(string)-1];   
        ...                                
        do {                                            
           ...
        } while ( ++lhs < --rhs )                      
    
    This sets the pointers within the confines of the string memory location and the increment/decrement is done at the bottom of the loop. Not a big difference, but clearer for the novice.

    I like lloyddean's approach using pointers in a subroutine for 2 reasons. In a sink or swim industry, with C++ coding, you better be comfortable with both subroutines and pointer based memory manipulation right away.
     
  25. phySi0 thread starter macrumors member

    Joined:
    Jun 19, 2011
    Location:
    You don't want to know!
    #25
    I'll take this challenge, but I'm not guaranteeing any replies to the thread with the answer, so don't hold your breath. :eek:
     

Share This Page