11

Context

I'm learning C, and I'm trying to reverse a string in place using pointers. (I know you can use an array; this is more about learning about pointers.)

Problem

I keep getting segmentation faults when trying to run the code below. GCC seems not to like the *end = *begin; line. Why is that?

Especially since my code is nearly identical to the non-evil C function already discussed in another question

#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);
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
brice
  • 24,329
  • 7
  • 79
  • 95
  • 2
    This is actually a dup of [this post](http://stackoverflow.com/a/164258/1270789), trying to write to a constant string - voting to reopen so it can get re-closed for the correct dup. – Ken Y-N Sep 21 '16 at 02:47
  • 2
    Possible duplicate of [Why do I get a segmentation fault when writing to a string initialized with "char \*s" but not "char s\[\]"?](http://stackoverflow.com/questions/164194/why-do-i-get-a-segmentation-fault-when-writing-to-a-string-initialized-with-cha) – brice Sep 21 '16 at 21:18
  • @KenY-N Makes sense to me :) Voted to close too. – brice Sep 21 '16 at 21:19

8 Answers8

22

One problem lies with the parameter you pass to the function:

char *string = "foobar";

This is a static string allocated in the read-only portion. When you try to overwrite it with

*end = *begin;

you'll get the segfault.

Try with

char string[] = "foobar";

and you should notice a difference.

The key point is that in the first case the string exists in the read-only segment and just a pointer to it is used while in the second case an array of chars with the proper size is reserved on the stack and the static string (which always exists) is copied into it. After that you're free to modify the content of the array.

Remo.D
  • 16,122
  • 6
  • 43
  • 74
  • Something like `char *string = strdup( "foobar" );` would work. – x4u Jan 23 '10 at 20:37
  • @x4u. Yes but you should free the memory returned by strdup() when done. Not important in this case, I admit, but still I wouldn't suggest using strdup() unless it's the only sensible option. – Remo.D Jan 23 '10 at 20:43
  • @brice: yw :) What do you mean about reading from the read only memory? – Remo.D Jan 23 '10 at 20:44
  • @Remo.D: I had no idea that char str[] and char *str would be different. I had the impression that they would create exactly the same object in memory, so I'm going to have to get my google-foo on and learn some more! There's a good discussion here: http://stackoverflow.com/questions/1704407/what-is-the-difference-between-char-s-and-char-s-in-c – brice Jan 23 '10 at 20:48
  • 2
    Just to be perfectly clear, `char *s = "blah"` allocates storage for 5 characters somewhere that may or may not be read-only. In most cases the storage is in a read-only segment of memory alongside the machine code for the program instructions. On the other hand, `char s[] = "blah";` allocates a modifiable array of 5 characters on the stack (i.e., local storage). This is always modifiable. Take a look at (http://stackoverflow.com/questions/2036096/literal-string-initializer-for-a-character-array) for more details about literal strings. – D.Shawley Jan 23 '10 at 20:53
  • got it: Kernighan & Richie, chapter 5.5, page 104, 2d edition, The C programming Language. "the pointer may be subsequently modified to point elsewhere, but the result is undefined if you try to modify the string contents." – brice Jan 23 '10 at 21:05
5

You can also utilize the null character at the end of a string in order to swap characters within a string, thereby avoiding the use of any extra space. Here is the code:

#include <stdio.h>

void reverse(char *str){    
    int length=0,i=0;

    while(str[i++]!='\0')
        length++;

    for(i=0;i<length/2;i++){
        str[length]=str[i];
        str[i]=str[length-i-1];
        str[length-i-1]=str[length];
    }

    str[length]='\0';
}

int main(int argc, char *argv[]){

    reverse(argv[1]);

    return 0;
}
user720694
  • 2,035
  • 6
  • 35
  • 57
4

In your code you have the following:

*end--;
*begin++;

It is only pure luck that this does the correct thing (actually, the reason is operator precedence). It looks like you intended the code to actually do

(*end)--;
(*begin)++;

Which is entirely wrong. The way you have it, the operations happen as

  • decrement end and then dereference it
  • increment begin and then dereference it

In both cases the dereference is superfluous and should be removed. You probably intended the behavior to be

end--;
begin++;

These are the things that drive a developer batty because they are so hard to track down.

ezpz
  • 11,767
  • 6
  • 38
  • 39
3

This would be in place and using pointers

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

 void reve(char *s)
 {
    for(char *end = s + (strlen(s) - 1); end > s ; --end, ++s)
    {
        (*s) ^= (*end);
        (*end) ^= (*s);
        (*s) ^= (*end);
    }
 }

int main(void)
{
    char *c = malloc(sizeof(char *) * 250);
    scanf("%s", c);
    reve(c);
    printf("\nReverse String %s", c);
}
nmd
  • 823
  • 1
  • 7
  • 17
2

Change char *string = "foobar"; to char string[] = "foobar";. The problem is that a char * points to read only memory which you then try to modify causing a segmentation fault.

Kyle Lutz
  • 7,966
  • 2
  • 20
  • 23
0

Below, you can see my code for this problem:

#include <string>
#include <iostream>

char* strRev(char* str)
{
    char *first,*last;

    if (!str || !*str)
        return str;

    size_t len = strlen(str);
    for (first = str, last = &str[len] - 1; first < last ; first++, last--)
    {
        str[len] = *first;
        *first = *last;
        *last = str[len];
    }
    str[len] = '\0';
    return str;
}

int main()
{
    char test[13] = "A new string";
    std::cout << strRev(test) << std::endl;
    return 0;
}
Reza
  • 3,473
  • 4
  • 35
  • 54
0

Here's my version of in-place C string reversal.

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

int main (int argc, const char * argv[])
{
    char str[] = "foobar";
    printf("String:%s\n", str);
    int len = (int)strlen(str);
    printf("Lenth of str: %d\n" , len);
    int i = 0, j = len - 1;
    while(i < j){
        char temp = str[i];
        str[i] = str[j];
        str[j] = temp;
        i++;
        j--;
    }

    printf("Reverse of String:%s\n", str);
    return 0;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dushshantha
  • 437
  • 5
  • 10
0

This makes for a small(ish) recursive function and works by storing the values on the way down the stack and incrementing the pointer to the start of the string (*s) on the way back up (return).

Clever looking code but awful in terms of stack usage.

#include <stdio.h>

char *reverse_r(char val, char *s, char *n)
{
    if (*n)
        s = reverse_r(*n, s, n+1);
   *s = val;
   return s+1;
}

int main(int argc, char *argv[])
{
    char *aString;

    if (argc < 2)
    {
        printf("Usage: RSIP <string>\n");
        return 0;
    }

    aString = argv[1];
    printf("String to reverse: %s\n", aString );

    reverse_r(*aString, aString, aString+1); 
    printf("Reversed String:   %s\n", aString );

    return 0;
}
Simon Peverett
  • 4,128
  • 3
  • 32
  • 37