0

I have a function to replace tabs with spaces in a string that looks like this:

#include <stdio.h>

char *detab(char *string) 
{
    for (int i = 0; string[i] != '\0'; i++)
        if (string[i] == '\t')
            string[i] = ' ';
    return string;
}

int main(int argc, char **argv)
{
    char *string = "\thello\thello";
    detab(string);
    printf("%s\n", string);
    return 0;
}

But when I run it on "\thello\thello\t", it produces a segmentation fault. Why does it do this? I'm pretty new to C, so I may be missing something trivial.

Majora320
  • 1,321
  • 1
  • 13
  • 33
  • 2
    You cant modify a string literal in c [Is it possible to modify a string of char in C?](https://stackoverflow.com/questions/1011455/is-it-possible-to-modify-a-string-of-char-in-c/1011545#1011545) – Rabbid76 Feb 13 '16 at 20:14
  • We don't know if this function is being called on a string literal or not. Could you please show how you call this function and how you declare the thing that you pass as an argument? – e0k Feb 13 '16 at 20:16
  • 1
    @e0k He said: But when I run it on "\thello\thello\t" – Rabbid76 Feb 13 '16 at 20:17
  • @Rabbid76 Thanks, that was it - in the program I'm using it in, it isn't run on a string literal, so that shouldn't be a problem. – Majora320 Feb 13 '16 at 20:20
  • 1
    A segmentation fault is caused by accessing memory that you shouldn't. Without seeing how this function is called and where the value of the pointer argument comes from, your question is incomplete and your result is not reproducible. See [mcve]. – e0k Feb 13 '16 at 20:24
  • 1
    Ok, now you're calling it on a string literal. See @Rabbid76's comments. – e0k Feb 13 '16 at 20:26
  • 2
    That is literally a string literal. – 2501 Feb 13 '16 at 20:27
  • 1
    Since your edit, it is more clear that this question already has an answer at [Is it possible to modify a string of char in C?](https://stackoverflow.com/questions/1011455/is-it-possible-to-modify-a-string-of-char-in-c/1011545#1011545). – e0k Feb 13 '16 at 20:32
  • 1
    I can't decide who to accept - there are like 4 good answers lol. – Majora320 Feb 13 '16 at 20:39
  • 1
    @Majora320, I've added an update. The issue is that, when you define an actual string literal and assign it to a char *, it resides in the text portion of memory, and cannot be modified safely. Instead, you should assign it to a char [] which will let the compiler know it should allocate space on the stack and populate it with the values in the string literal, allowing it to be modified. This is the real reason your code was segfaulting. – techdude Feb 13 '16 at 20:42

3 Answers3

2

This could be because the calling code did not allocate enough space for the string. It must always allocate at least one space larger than the visible characters in the string to allow space for the \0.

That being said, since strings are mutable, there is no need to return the string. It it will modify the string as you are working.

Here would be a working version of your code:

void detab(char * myStr) 
{
    for (int i = 0; myStr[i] != '\0'; i++)
        if (myStr[i] == '\t')
            myStr[i] = ' ';
}

char theString[] = "\thello\thello\t";
printf("Before: %s", theString);
detab(theString);
printf("After: %s", theString);

Also, keep in mind the following:

char buffer[4] = "test"; //THIS IS NOT SAFE. It might work, but it will overwrite stuff it shouldn't
char buffer[5] = "test"; //This is Ok, but could be an issue if you change the length of the string being assigned.
char buffer[] = "test"; //This is preferred for string literals because if you change the size of the literal, it will automatically fit.

UPDATE: Base on the main method you added, here is your issue:

You need to change

char * string = "\thello\thello";

To

char string[] = "\thello\thello";

The reason is because, when you define a string literal and assign it to a char *, it resides in the text portion of memory, and cannot be modified safely. Instead, you should assign the string literal to a char [] (which can be passed as a char * since that is what its actual type is). This syntax will let the compiler know it should allocate space on the stack and populate it with the values in the string literal, allowing it to be modified.

char * joe = "blah" simply creates the char * pointer, and points it to the data in the text portion (which is immutable).

char joe[] = "blah" tells the compiler to create an array of the appropriate length on the stack, to load it with the string literal, to create the char * pointer, and then to point the pointer at the start of the array of data on the stack.

techdude
  • 1,334
  • 20
  • 29
  • 1
    I should add that it is often preferred to pass in the length of the string and use that instead of checking for the \0 termination, because it can then allow the function to work on binary strings containing other data instead of just c strings. – techdude Feb 13 '16 at 20:26
  • 1
    Depending on how you are allocating the strings that you pass in, you must be careful that they remain null terminated, and that they have enough space allocated. There isn't enough context provided in your question to tell for sure what your problem is, but this should point you in the right direction. – techdude Feb 13 '16 at 20:32
  • 1
    If you are using malloc, let me know some additional details, and I can cover those – techdude Feb 13 '16 at 20:35
  • I am using it in conjunction with a method that reads all the bytes from the file and puts them into a `char*` with size (obtained by `malloc`) determined by calling `ftell` at the end of the file, and then inserts a `'\0'` at the end manually. – Majora320 Feb 13 '16 at 20:42
  • 1
    In that case, you will need to call malloc with ftell + 1 so there is space for a '\0'. – techdude Feb 13 '16 at 20:45
  • I already did that, but thank you! :3 – Majora320 Feb 13 '16 at 20:51
  • And I'll have to accept your answer, since you've been the most helpful so far. :3 – Majora320 Feb 13 '16 at 20:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/103388/discussion-between-techdude-and-majora320). – techdude Feb 13 '16 at 20:53
1

This works:

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

char *detab(char *string) 
{
    for (int i = 0; string[i] != '\0'; i++)
        if (string[i] == '\t')
            string[i] = ' ';
    return string;
}

int main ( int argc, char ** argv ) {
    char str[21] = "\thello\thello\t";

    printf( "%s\n", detab( str ) );

    return 0;
}

As others have said, it's probably segfaulting because you're modifying a string literal. With char str[21], the string literal is copied into the stack-allocated str, where it can then be modified by your function.

Chad
  • 693
  • 5
  • 17
  • There is no need to do this. Provided it is assigned to a char array that is allocated on the stack, it will be just fine. – techdude Feb 13 '16 at 20:47
  • I guess you mean ``char str[21] = "\thello\thello\t"``... yeah, that works too. Thanks, I'll update my answer. – Chad Feb 13 '16 at 20:53
0

Are you sure the strings are always null terminated.

Try some belt and braces...

char *detab(char *string) 
{
    int s_len= strlen(string) + 1;

    for (int i = 0; string[i] != '\0'; i++)
    {
        if (string[i] == '\t')
        {    string[i] = ' '; }
        if (i == s_len) { /* failure - print some diagnostic */ return NULL; }
    }
    return string;
  }
cdcdcd
  • 547
  • 1
  • 5
  • 15
  • This would not be an adequate check because strlen USES the null terminated character to determine the length which would be no different than what the OP originally did. http://www.cplusplus.com/reference/cstring/strlen/ – techdude Feb 13 '16 at 20:30
  • A string literal automatically inserts the `'\0'` for you. – Majora320 Feb 13 '16 at 20:33
  • strlen returns the size of the string excluding the null terminating character hence the addition to s_len (which someone has kindly tidied up into one rather than two lines). – cdcdcd Feb 13 '16 at 20:34
  • @Majora that is a very good point. Feel free to edit at will. – cdcdcd Feb 13 '16 at 20:37