views:

287

answers:

5

Hi, my code segfaults and I don't know why.

 1  #include <stdio.h>
 2
 3  void overwrite(char str[], char x) {
 4    int i;
 5    for (i = 0; str[i] != '\0'; i++)
 6      str[i] = x;
 7  }
 8
 9  int main(void) {
10    char *s = "abcde";
11    char x = 'X';
12    overwrite(s, x);
13    printf("%s\n", s);
14    return 0;
15  }

The gdb debugger tells me, that problem is on the line 6, where I want to store a char, into c-string (if I use lvalue pointer dereferencing, it's the same problem.) This is what he says:

(gdb) run
Starting program: /tmp/x/x 

Breakpoint 1, overwrite (str=0x8048500 "abcde", x=88 'X') at x.c:5
5         for (i = 0; str[i] != '\0'; i++)
(gdb) s
6           str[i] = x;
(gdb) 

Program received signal SIGSEGV, Segmentation fault.
0x080483e3 in overwrite (str=0x8048500 "abcde", x=88 'X') at x.c:6
6           str[i] = x;
(gdb) q

I am learning from K&R-C book and this is simplified example from chapter 2.8 (the remove function). I have no idea where is the problem.

+16  A: 

because char*s = "abcde"; creates string in readonly memory. try

char s[] = "abcde";

EDIT: explanation: char* is pointer, and "abcde" is created in readonly memory -> immutable.

char[] is array, which is wholly stored on stack and initialized from the memory, so is mutable

Yossarian
A: 

my guess is the parameter definition where you define the type as an array of chars. While you are passing a pointer to a char

You could try changing the first line to this:

 void overwrite(char *str, char x) {

A char array and a char pointer are not semantically the same.

Toad
`char str[]` and `char *str` are equivalent as function arguments.
sepp2k
You're wrong, `char str[]` decays to `char *` when used in a parameter list.
avakar
ok.... But I'm right that they are different in some cases right? Could you elaborate in which case it is different?
Toad
@reinier, no they are the same as function parameters. Both have type char*, none of them is an array (although the first is declared as an array - the compiler won't make it an array).
Johannes Schaub - litb
i meant cases other than as a function parameter. I understand now that they are the same as used as a parameter ;^)
Toad
Johannes Schaub - litb
thanks for the explanation!
Toad
+2  A: 

When you define a pointer to a string literal, declare it as const char *.

const char *s = "abcde";

That way, your compiler complains when you try to send that string to the overwrite() function.

const char *s = "abcde";
char t[] = "fghij";
char x = 'X';

overwrite(s, x); /* oops */
overwrite(t, x); /* ok */
pmg
+1  A: 

Not disagreeing, but just to elaborate: Consider what would happen if the compiler allowed this. You could write:

char *s1="abcde";
char *s2="abcde";
s1[0]='x';
puts(s1);
puts(s2);

If the compiler recognizes that the two literals are the same and re-uses them, but then also allows line 3, your output would be:

xbcde
xbcde

Which is probably not what you would want. This would be particularly mysterious if the two literals were in widely-separated parts of the program.

Jay
This is why in .Net strings are immutable. It *does* reuse the strings through something called "string interning".
Tom Ritter
@Tom Ritter - Yep, that's why .Net is a completely different platform with completely different goals than C.
Chris Lutz
A: 

Try out:

#include <iostream>
#include <cstring>

using namespace std;

void overwrite(char[], char);

int main(void)
{
        char *s = strdup("abcde");
        char X = 'X';
        overwrite(s, X);
        cout << s << endl;

        if(s!=NULL)
                delete [] s;

        return 0;
}

void overwrite(char str[], char x)
{
        for(int i=0; str[i]!='\0'; i++)
                str[i] = x;
}
Daniel
Question is tagged as C ... and if you're going to check for s being NULL you could check immediately after the strdup() call, rather than using an invalid pointer.
pmg
also strdup does a malloc() ... calling delete[] on it is wrong.
Nicholaz