Segmentation fault occurring when modifying a string using pointers?
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);
}
Solution 1:
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.
Solution 2:
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;
}
Solution 3:
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.
Solution 4:
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);
}
Solution 5:
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.