1

I'm using with a smaller piece of code to test functionality for a larger (beginner) program, but I have a problem displaying the token I've pulled out of a string.

I found and used:

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

int main()
{

char *string, *found;

string = strdup ("1/2/3");
printf("Original string: '%s'\n",string);

while ((found = strsep(&string,"/")) != NULL )
  printf ("%s\n",found);

return (0);
}

and this works fine, prints the tokens one at a time as strings.

Then when I try and move to a user entered string:

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

int main()
{
  char string[13];
  char *found, *cp = string;

  fprintf(stderr, "\nEnter string: ");
  scanf("%12s",string);
  printf("Original string: '%s'\n",string);

  while((found =  strsep(&cp,"/,-")) != NULL )
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
    printf("%s\n",found);

  return(0);
}

I get a seg fault on the printf("%s\n",found); line. I'm getting the hang of basics of pointers, arrays and strings, but clearly I'm missing something, and would love for someone to tell me what it is!

Also - if I change the argument of printf("%s\n",found); e.g. to printf("%i\n",found); I get some randomness returned, but always the correct amount, e.g. If I enter 1/2/3 I get three lines of junk, entering 1111/2222 gives two lines. I tried %c, %i, %d, %p and they all do the same, but %s seg faults.

I'm completely stumped.

Weaver
  • 145
  • 8
  • And now you know why there are many, many C programmers who **always** put braces around code following `if`, `while`, and `for` statements. It's easier write code in ways that actually help prevent bugs than it is to find them after you've put them into your code - because if you knew you were writing buggy code you wouldn't do it. – Andrew Henle Mar 20 '18 at 21:58

2 Answers2

4

The segfault is because you're missing braces around your while. You'll keep printing "Test 1" until strsep returns NULL, then you try to print that result (and segfault).

With several warning flags (probably -Wall), gcc helps out here:

sep.c:13:3: warning: this ‘while’ clause does not guard... [-Wmisleading-indentation]
   while((found =  strsep(&cp,"/,-")) != NULL )
   ^~~~~
sep.c:15:5: note: ...this statement, but the latter is misleadingly indented as if it is guarded by the ‘while’
     printf("%s\n",found);
     ^~~~~~

With braces added around the while, the program works as expected:

./sep 

Enter string: abc/def
Original string: 'abc/def'
Test 1abc
Test 1def
Stephen Newell
  • 7,330
  • 1
  • 24
  • 28
2

This is the problem:

while((found =  strsep(&cp,"/,-")) != NULL )
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
    printf("%s\n",found);

and you think you are doing both printfs inside the loop, but in reality this code is equivalent to

while((found =  strsep(&cp,"/,-")) != NULL )
{
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
}
    printf("%s\n",found);

that means, printf("%s\n",found); is basically doing printf("%s\n",NULL); which is undefined behaviour and may cause a segfault.

Note that in C indentation does not matter to the compiler. So you would need to use { and } around the code:

while((found =  strsep(&cp,"/,-")) != NULL )
{
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
    printf("%s\n",found);
}

Doing that I get

$ ./a 

Enter string: aa/bb/cc/dd
Original string: 'aa/bb/cc/dd'
Test 1aa
Test 1bb
Test 1cc
Test 1dd

Also note that your first code is leaking memory, you are not freeing the allocated memory returned by strdup. You would have to save a pointer to that:

#include <stdio.h>
#include <stdlib.h> // for the free function
#include <string.h>

int main()
{

    char *orig = *string, *found;

    orig = string = strdup ("1/2/3");
    printf("Original string: '%s'\n",string);

    while ((found = strsep(&string,"/")) != NULL )
      printf ("%s\n",found);

    free(orig);

    return 0;
}

edit

Neither Stephen Newell nor me seems to have the same problem with the corrected version of the code. The OP provided a link to onlinegdb.com showing that the corrected version ends with a segfault.

I tried the same code on ideone.com and I also got the segfault. That seemed strange to me, so I opened my man page of strsep and found this:

man strsep

SYNOPSIS

   #include <string.h>

   char *strsep(char **stringp, const char *delim);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

strsep():

Since glibc 2.19:
    _DEFAULT_SOURCE
Glibc 2.19 and earlier:
    _BSD_SOURCE

The important part is this here: Since glibc 2.19: _DEFAULT_SOURCE

So if you add

#define _DEFAULT_SOURCE

before including any standard C header file, then it works on onlinegdb.com and ideone.com.

So the code should be:

#define _DEFAULT_SOURCE   // <-- important
#include <stdio.h>
#include <string.h>

int main()
{
  char string[13];
  char *found, *cp = string;

  fprintf(stderr, "\nEnter string: ");
  scanf("%12s",string);
  printf("Original string: '%s'\n",string);

  while((found =  strsep(&cp,"/,-")) != NULL )
    {
    printf("Test 1"); /*To pinpoint where the seg fault arises*/
    printf("%s\n",found);
    }

  return(0);
}

See:

Pablo
  • 13,271
  • 4
  • 39
  • 59
  • Thanks, that's a really well presented answer! I'm confused though because that was just a typo in my question, I have the braces in my code, and also it was seg faulting even before I put the test print in. I tried tunneling into uni but it still faults there, and I put it in to an online compiler here at home and it faults again. But I see since two people have working outputs with the same code that I get faults with, then the problem must lie elsewhere! – Weaver Mar 20 '18 at 22:06
  • 1
    @William Please check your code carefully and compile it with `-Wall`. If you see a warning like the one posted on Stephen Newell answer, then there are definitely braces missing. Or you did some mistake when transferring the file to your uni. Regardless, the nature of undefined behaviour is that a segfault may occur immediately, but it also may occur some time later executing some other (unrelated) function, which is why you see the segfault at `printf("Test 1")`. – Pablo Mar 20 '18 at 22:13
  • Thanks, I see a warning about making a pointer from an int without a cast, but the working code with `strdup` had the same and I thought it wasn't an issue. The code is identical, I was working on it for a while at uni before posting here on SE, and I've tried several variations. Plus, it doesn't seg fault with `%i` etc, so the problem seems to be with fprinting `%s`. What's odd is it works for you and Stephen Newell, but not on my uni compiler or on onlinegdb.com. In the end, it'll have to work on my uni system so maybe I have to try `strtok`. – Weaver Mar 20 '18 at 22:31
  • Post the link to onlinegdb.com, so that I can take a look. *it doesn't seg fault with `%i`* that would be because `%i` is interpreting the `NULL` pointer an `int`, where `%s` dereferences the `NULL` pointer which is undefined behaviour. – Pablo Mar 20 '18 at 22:44
  • Cheers, man. https://www.onlinegdb.com/fork/r1VBLGJ5G is what I've been working with. I've gotten it to work with `strtok` but that's not really any good, because I need to work with empty fields. I'll try and think why I'm getting `NULL`, and you're not. – Weaver Mar 20 '18 at 23:26
  • @William I've found the problem, I've made an update of my answer. – Pablo Mar 20 '18 at 23:59