C: Assign strtok token to char * Segfault

Viewed 867

Why do I get a segfault with the below code?

#include <stdio.h>

int main()
{
    char * tmp = "0.1";
    char * first = strtok(tmp, ".");
    return 0;
}

Edited:

#include <stdio.h>

int main()
{
    char tmp[] = "0.1";
    char *first = strtok(tmp, ".");
    char *second = strtok(tmp, "."); // Yes, should be NULL
    printf("%s\n", first);       
    printf("Hello World\n");
    return 0;
}

The segfault can be reproduced at the online gdb here: https://www.onlinegdb.com/online_c_compiler

4 Answers

The problem with your first code is that tmp points at a string literal, which is read-only. When strtok tries to modify the string, it crashes.


The problem with your second code is a missing include:

#include <string.h>

This missing header means strtok is undeclared in your program. The C compiler assumes all undeclared functions return int. This is not true for strtok, which returns char *. The likely cause of the crash in your example is that the code is running on a 64-bit machine where pointers are 8 bytes wide but int is only 4 bytes. This messes up the return value of strtok, so first is a garbage pointer (and printf crashes when it tries to use it).

You can confirm this for yourself by doing

char *first = strtok(tmp, ".");
printf("%p %p\n", (void *)tmp, (void *)first);

The addresses printed for tmp and first should be identical (and they are if you #include <string.h>).


The funny thing is that gcc can warn you about these problems:

main.c: In function 'main':
main.c:6:19: warning: implicit declaration of function 'strtok' [-Wimplicit-function-declaration]
     char *first = strtok(tmp, ".");
                   ^
main.c:6:19: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
main.c:7:20: warning: initialization makes pointer from integer without a cast [-Wint-conversion]
     char *second = strtok(tmp, "."); // Yes, should be NULL
                    ^

... and onlinegdb will show you these warnings, but only if compilation fails!

So to see compiler warnings on onlinegdb, you have to add a hard error to the code (e.g. by putting a @ in the last line of the file).

The behaviour of the function strtok goes something like this:

  1. Accept a string str or NULL and a string of delimiters characters.
  2. The strtok function then begins to process the given string str, wherein which it reads the string character by character until it encounters a character present amongst the provided delimiter characters.
  3. If the number of characters it has encountered until reaching the delimiter string is > 0, then replace the delimiter character by '\n' and returns a pointer to the first character in this iteration which was not a delimiter character.
  4. Else, if the number of characters it has encountered until reaching the delimiter string is == 0, then continue iterating the rest of the string without replacing this delimiter character by '\n'.

I've created some code snippets which will help you better understand the nature of the function, https://ideone.com/6NCcrR and https://ideone.com/KVI5n4 (<- taking excerpts from your code your code)

Now to answer your question, including string.h header and setting char tmp[] = "0.1"; should solve your issue.

With char * tmp = "0.1";, tmp points to a string literal that cannot be modified and strtok tries to modify the string by replacing . with '\0'.

Another approach, avioding the segfault, would be to use strchr to find the dot and the precision field to print a limited number of characters. The sub-strings may be copied to other variables as well.

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

int main ( void) {
    char * tmp = "0.1";
    char * first = strchr(tmp, '.');
    char * second = first + 1;
    if ( first) {
        printf ( "%.*s\n", first - tmp, tmp);
        printf ( "%s\n", second);
    }
    printf ( "Hello World\n");
    return 0;
}

tmp is not a string literal as few answers or comments point out.

char *tmp = "0.1" this is a string literal.

char tmp[] = "0.1" is a character array and all array operations can be performed on them.

The segfault arises because the function declaration for strtok is not found as string.h is not included, and the gcc or other c compilers implicitly declare the return type as int by default.

Now depending on the platform the integer size may vary, if int size is 4 byte and pointer size is 8 byte respectively

char *first = (int)strtok(tmp,".");

Truncation takes place on the pointer address returned by strtok and then when your printing, you are de-referencing the address value contained in first which could be a memory region out of bound resulting in segmentation fault or undefined behavior.

If you can typecast the output of strtok to a type that is 8 bytes(long in my case) then there would not be a segfault, although this is not a clean way to do.

Include proper headerfiles to avoid undefined behavior.

Related