Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

phySi0

macrumors member
Original poster
Jun 19, 2011
76
0
You don't want to know!
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.
 

Attachments

  • Screenshot 2011-08-13 at 13.47.11.png
    Screenshot 2011-08-13 at 13.47.11.png
    20.9 KB · Views: 280
Last edited:
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
 
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


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.
 
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.

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'?
 
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'?

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?
 
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.

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
 
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

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.
 
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.

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
 
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

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?
 
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?

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
 
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?

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).
 
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).

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!
 
Last edited:
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;
}
 
Now that you've got something that works I'll throw out this one:

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
 
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!

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.)
 
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;
}

Wow! Thanks, this makes much more sense. But what does the EXIT_SUCCESS do?
 
Wow! Thanks, this makes much more sense. But what does the EXIT_SUCCESS do?

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
 
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.)

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?

----------

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

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?
 
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.
 
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

"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.
 
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".
 
"void main ()" is not valid C.

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

If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument; reaching the } that terminates the main function returns a value of 0. If the return type is not compatible with int, the termination status returned to the host environment is unspecified.

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

Not recommended, sure, but invalid?

B
 
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
 
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.
 
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".

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:
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.