3

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.

Tom Ritter
  • 99,986
  • 30
  • 138
  • 174
Martin
  • 1,473
  • 2
  • 12
  • 20

5 Answers5

17

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

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
nothrow
  • 15,882
  • 9
  • 57
  • 104
2

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
  • 106,608
  • 13
  • 126
  • 198
1

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
  • 26,876
  • 10
  • 61
  • 112
  • This is why in .Net strings are immutable. It *does* reuse the strings through something called "string interning". – Tom Ritter Sep 10 '09 at 15:20
  • @Tom Ritter - Yep, that's why .Net is a completely different platform with completely different goals than C. – Chris Lutz Sep 11 '09 at 05:14
-1

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
  • 374
  • 2
  • 5
  • 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 Sep 10 '09 at 16:36
  • also strdup does a malloc() ... calling delete[] on it is wrong. – Nicholaz Nov 17 '09 at 14:37
-2

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
  • 15,593
  • 16
  • 82
  • 128
  • `char str[]` and `char *str` are equivalent as function arguments. – sepp2k Sep 10 '09 at 14:34
  • You're wrong, `char str[]` decays to `char *` when used in a parameter list. – avakar Sep 10 '09 at 14:35
  • ok.... But I'm right that they are different in some cases right? Could you elaborate in which case it is different? – Toad Sep 10 '09 at 14:36
  • @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 Sep 10 '09 at 15:00
  • i meant cases other than as a function parameter. I understand now that they are the same as used as a parameter ;^) – Toad Sep 10 '09 at 15:04
  • In other cases they surely are different. The difference shows up less in C than in C++, though. In C, an array `E[N]` is implicitly converted to a `E*` anywhere it appears, except as argument of `sizeof` or `address-of &`. So, sizeof will give you `N * sizeof(E)`, and address-of will give you `E(*)[N]`. Conceptionally, the array is different to - in some kind it's the inverse: A pointer points to an array of elements, while an array stores those elements. So in the situation that conversion occurs, it's not because of their similarity, but because of the necessity to address an element :) – Johannes Schaub - litb Sep 10 '09 at 15:11