开发者

Reverse a string using pointers in a function, output in main is garbled

I'm trying to self-study C using C Primer Plus from Stephen Prata and one of the end-of-chapter exercises is to "Write a function that replaces the contents of a string with the string reversed.". This is a chapter on character strings with a good dose of pointers. I'm trying to use pointers as much as possi开发者_如何学运维ble so I can better understand, but I'm stuck.

My problem is that when I print the value of the return pointer in main, it is garbled.

When I use gdb(just learning how to use that too), I can see that the memory address returned from my function is the same address that was used in the function and it's getting assigned to my pointer in main okay as far as I can tell.

I've tried so many things, what am I missing? FWIW I have not learned about malloc yet in the book, though I see it referenced on various www pages I've frequented trying to better understand C.


$ cc -o exercise8 exercise8.c && ./exercise8
This is s1 before: abcd
This is s2 in function: dcba
This is s3 after: d`!

/*   A function that replaces the contents of a string with the string reversed. */
#include <stdio.h>
#include <string.h>
char *str_rev(char * string);
int main(void)
{
   char * s1  = "abcd";
   char * s3;
   printf("This is s1 before: %s\n", s1);

   s3 = str_rev(s1);
   printf("This is s3 after: %s\n", s3);

}

char *str_rev(char * string)
{
   char ar3[5];
   char * s2;
   int len = 0;

   s2 = ar3;

   len = (strlen(string) - 1);
   string = string + len;

   while ( len >= 0 )
   {
      *s2 = *string;
      len--;
      string--;
      s2++;
   }
   s2++;
   *s2 = 0;
   s2 = s2 - 5;

   printf("This is s2 in function: %s\n", s2);
   return s2;
}
$ gdb exercise8
GNU gdb (GDB) 7.1-ubuntu

Reading symbols from exercise8...done.
(gdb) break 12
Breakpoint 1 at 0x804844a: file exercise8.c, line 12.
(gdb) break 40
Breakpoint 2 at 0x80484d9: file exercise8.c, line 40.
(gdb) run
Starting program: exercise8
This is s1 before: abcd         // My original string.
This is s2 in function: dcba        // Good, my reversed string while in the function.

Breakpoint 2, str_rev (string=0xbffff043 "dcba") at exercise8.c:40
40               return s2;
(gdb) print s2
$1 = 0xbffff043 "dcba"          // Location of pointer s2.
(gdb) continue
Continuing.

Breakpoint 1, main () at exercise8.c:12
12               printf("This is s3 after: %s\n", s3);
(gdb) print s3
$2 = 0xbffff043 "dcba"          // Back in main same pointer as s2 from function.
(gdb) step
This is s3 after: d`Q           // Line 12 executed.  Output garbled.
14      }
(gdb)


You're returning a pointer to a local variable, (automatic variable to speak in ISO standard terms,) which is allocated on the stack, as soon as you return from your function that memory is released leaving you with a dangling pointer pointing to memory that may or may not still contain the string you put there, it depends entirely on circumstances. You should provide the output buffer as a function argument, or allocate it with malloc, or in C++ with new.

edit; added some example code

void reverse(const char* s1, char* s2) {
  const int l = strlen(s1);
  const char* p = s1 + l - 1;
  do {
    *s2++ = *p;
  } while (p-- != s1);
  *s2 = 0;
}

int main() {
  // some code here
  char s1[5] = "abcd";
  char s2[5] = "";

  reverse(s1, s2);

  // some more code here

  return 0;
}

or

char* reverse(const char* s) {
  const int l = strlen(s);
  char* rs = malloc(l+1);
  const char* p = s + l - 1;
  do {
    *rs++ = *p;
  } while (p-- != s);
  *rs = 0;
  return rs - l;
}

int main() {
  // some code here
  char s1[5] = "abcd";

  char* s2 = reverse(s1);

  // some more code here

  free(s2);

  return 0;
}


char ar3[5];
char * s2 = ar3;

The above code will make s2 points to a character string on the stack. This ar3 variable will be deleted once your function finishes.

You should output to some variable that you have pre-allocated. Modify it as follow

int main(void)
{
   char * s1  = "abcd";
   char s3[5];
   printf("This is s1 before: %s\n", s1);

   str_rev(s1, s3);
   printf("This is s3 after: %s\n", s3);

}

void str_rev(char * string, char * s2)
{
   ........

   // don't return

   // Also assign the last character with the NULL terminator
   ar2[strlen(string)] = '\0';
}

Of course, once you get to the chapter regarding malloc, you can allocate the necessary memory for s3 depending on the length of s1. Until then, read on and have fun.


The problem description sounds like you can just reverse the string in place. Keep it simple.

void reverse_range(char *first, char *last) // [first, last)
{
    for (; first != last && first != --last; ++first)
    {
        char temp = *first; *first = *last; *last = temp;
    }
}

void reverse(char *str)
{
    reverse_range(str, str + strlen(str));
}

int main()
{
    char text[] = "0123456789";

    printf("before: %s\n", text);
    reverse(text);
    printf("after : %s\n", text);
}


s2 points to your local ar3 array. Therefore when str_rev returns, it is no longer valid to look at the memory where ar3 was via s2. Now s2 is called a dangling pointer, and this is one of the huge pains in learning to use C pointers correctly.

For a simple solution that doesn't use malloc and meets the exercise requirement of "replaces the contents of a string", try copying the result into the function argument pointer (the original string; but be careful since your current code has since changed the pointer string). You know this pointer points at memory with enough characters and isn't local to your function.


Part of the problem is that you can't actually "replace the contents of a string with the string reversed" when you are dealing with string literals, ie. strings delcared in the form char * s1 = "abcd";

Without using literals, I made a relatively easy to understand recursive example:

/*   A function that replaces the contents of a string with the string reversed. */
#include <stdio.h>
#include <string.h>
void str_rev(char * string);

int main(void)
{

   char s1[] = "abc";
   char s2[] = "even";
   char s3[] = "quodd";


   printf("This is s1 before: %s\n", s1);
   str_rev(s1);
   printf("This is s1 after: %s\n", s1);

   printf("This is s2 before: %s\n", s2);
   str_rev(s2);
   printf("This is s2 after: %s\n", s2);

   printf("This is s3 before: %s\n", s3);
   str_rev(s3);
   printf("This is s3 after: %s\n", s3);

    return 0;

}

void str_rev(char * string) {

    //Store the first char of the string locally
    char firstChar = string[0];
    //Store the last char of the string locally
    int lastCharPos = strlen(string)-1;
    char lastChar = string[lastCharPos];

    //Shorten the string (temporarily)
    string[lastCharPos] = '\0';


    if (string[1] != '\0') {
        //Call on the now shortened string, eg.
        //"abc" becomes "b"
        //"even" becomes "ve"
        //"quodd" becomes "uod"
        str_rev(string+1);
    }

    //Swap the first and last characters
    string[0] = lastChar;
    string[lastCharPos] = firstChar;

}

On my system the output is as follows:

This is s1 before: abc
This is s1 after: cba
This is s2 before: even
This is s2 after: neve
This is s3 before: quodd
This is s3 after: ddouq


#include <stdio.h>
#include <string.h>

void my_strrev(char* begin){
    char temp;
    char* end;
    end = begin + strlen(begin)-1;

    while(end>begin){
        temp = *end;
        *end = *begin;
        *begin = temp;
        end--;
        begin++;
    } 
}

main(){
    char string[]= "foobar";
    my_strrev(string);
    printf("%s", string);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜