strtok_s crashes program when string is of char * - c

I am trying to get a token from a string that is delimited by space(" "). But the following code crashes the application when the string is of char * type.
#include <stdio.h>
#include <string.h>
int main(){
char *str1 = "Hello World!"; //char str1[] works
char *token;
char *savePtr;
token = strtok_s(str1, " ", &savePtr);
printf("%s", token);
return 0;
}
I also get the following warnings:
C:\Users\Haris\Desktop\C files>gcc firstProgram.c -o firstprogram.exe
firstProgram.c: In function 'main':
firstProgram.c:10:9: warning: implicit declaration of function 'strtok_s' [-Wimplicit-function-declaration]
token = strtok_s(str1, " ", &savePtr);
^
firstProgram.c:10:7: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
token = strtok_s(str1, " ", &savePtr);

The strtok_s function modifies the thing the pointer points to, turning delimiters into zeroes. So it cannot take a pointer to a constant. But you pass it str1, which is a pointer to a string constant. When it tries to modify that string to turn the delimiters into zeroes, it tries to modify a constant. That is, of course, illegal.

Related

Writing C function to return a character and assigning a value to a char variable, but I am having some problem

I am new to C and I am having a bit of trouble. I am wondering why C keeps telling me that I should put '(' in front of '=' while assigning the function's return value to my variable. It also keeps on saying it expected expression before 'char'.
#include <stdio.h>
char returnText(const char* text) {
return *text;
}
int main()
{
const char = returnText("Hello World");
printf("%s", char);
return 0;
}
const char = returnText("Hello World"); const char is a keyword, you forgot name it.
%s in printf is for strings/char*. %c is for char only.
const char returnText(const char* text) {
return *text;
}
int main()
{
const char t = returnText("Hello World");
printf("%c", t);
return 0;
}
this code is I think you want. function get "Hello World" or const char* and returns a first char.
in the main t is a const char an get the return of function
and printf prints a char. if you want print the full "hello world" string use %s
The basic syntax for declaring a variable is data_type identifier, which can optionally be followed by a definition. You wrote the data type (char), but no variable name/identifier. Your compiler is throwing an error because it encounters a variable definition = returnText("Hello World") before it encounters a variable identifier. Additionally, since char is a core language keyword, you can't use it as an identifier.
Some other notes you might want to consider:
Your function returnText is of return type char, meaning it will only return the single character pointed to by text: In this case it is the 'H' in "Hello World".
A pointer to a char char * can be used to point to the first character in a string, then passed to printf with the %s flag. Consider changing your variable on line 9 to type "pointer to char" char *
You could alternatively use an array of chars char varname[array_size] or malloc to allocate space in memory, then assign your "Hello World" string to it.
Example using an array of chars:
#include <stdio.h>
#define MAX_TEXT_LEN 12
int main()
{
char text[MAX_TEXT_LEN] = "Hello World";
printf("%s", text);
return 0;
}
Example using malloc:
#include <stdio.h>
#define MAX_TEXT_LEN 12
int main()
{
char* text = (char*)malloc(MAX_TEXT_LEN);
text = "Hello World";
printf("%s", text);
free(text);
return 0;
}
Note that in both of these examples, text represents a pointer to the first character in a string.

The strtok Function With char * Argument

I was just playing around with some code, and ended up typing something along the lines of the following piece of code. The issue seems to be that the char *string line isn't actually interchangeable with a char string[], but I can't seem to wrap my head around why strtok(...) throws a "segmentation violation" if my argument is initialized as a char* to a string, or why it would even require an initialization of char[] instead?
#include <stdio.h>
#include <string.h>
//extern char *strtok (char *__restrict __s, const char *__restrict __delim);
char *string = "Hello world whats up?";
/*
SEGV - Must be char string[] in order to execute.
e.g. char string[] = "Hello world whats up?";
*/
char *delim = "\t ";
char *token;
int main (argc, argv)
int argc;
char **argv;
{
token = strtok(string, delim);
while ( token != NULL ) {
printf("%s\n", token);
token = strtok(NULL, delim);
}
}
The strtok function (potentially) modifies the string passed to it as its first argument. That is the critical point, here.
In your code snippet (not using the [] version), your string variable is initialized to be the address of a string literal. That literal is a constant and is likely to be placed in read-only memory. Thus, when you call strtok and that function finds a delimiter character, it attempts to replace that character with a nul, which would require writing to memory to which it does not have the required access – and your program crashes.
However, in your version using the [] syntax, you are declaring a (modifiable) array of characters and initializing it with a copy of the string literal.
In summary:
char* pc = "Hello, World!"; // pc points to a CONSTANT string literal
char ca[] = "Hello, World!"; // ca is a 'normal' array initialized with data
In C all literal strings are non-modifiable, they are in essence read-only.
When you define and initialize string you make it point to the first character of such a literal string.
This is the reason it's recommended to use const char * for literal strings.
If you want to modify the string in any way, and strtok modifies the string it tokenizes, then you must use an explicit modifiable array.

invalid pointer when using strtok_r

When running my code (shown in the first code block), I get this error:
*** Error in `./a.out': free(): invalid pointer: 0x0000000001e4c016 ***
I found a fix (which is shown in the second code block), but I don't understand why the error is happening in the first place.
I read the documentation regarding strtok_r, but I don't understand why assigning "str" to a new char* fixes the problem.
Doesn't "rest = str" mean that rest and str point to the same block of memory. How does this fix the problem???
Broken code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char* str = (char*) malloc(sizeof(char) * 128);
char* token;
printf("Enter something: ");
fgets(str, 128, stdin);
while ((token = strtok_r(str, " ", &str))) {
printf("%s\n", token);
}
free(str);
return (0);
}
Fixed code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char* str = (char*) malloc(sizeof(char) * 128);
char* token;
char* rest = str;
printf("Enter something: ");
fgets(str, 128, stdin);
while ((token = strtok_r(rest, " ", &rest))) {
printf("%s\n", token);
}
free(str);
return (0);
}
It looks evidently that a call of strtok_r changes the pointer str that is passed to the call by reference as the third parameter.
while ((token = strtok_r(str, " ", &str))) {
^^^^
printf("%s\n", token);
}
So after a call of the function the pointer str can point inside the original string. So it will not store the value that it had after a call of malloc.
Thus using the auxiliary variable rest allows to keep the initial value in the pointer str.
Pay attention to that you are calling the function incorrectly. Here is its description
On the first call to strtok_r(), str should point to the string to be
parsed, and the value of saveptr is ignored. In subsequent calls, str
should be NULL, and saveptr should be unchanged since the previous
call.
So for the second and subsequent calls of the function the first argument shall be NULL.
You should write:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char str[128];
char *token;
char *rest = str;
printf("Enter something: ");
fgets(str, sizeof str, stdin);
for (token = strtok_r(rest, " ", &rest);
token = strtok_r(NULL, " ", &rest);
/* just nothing here */)
{
printf("%s\n", token);
}
return (0);
}
First, you don't need to allocate the memory for str, as you can define a local array to store the data. You can use the sizeof operator so you don't run the risk of not updating it in two places if you decide to change the size of str. In the case of using malloc you had better #define a constant to hold the value while you use the constant everywhere you are using the size of the allocated buffer.
Second, never cast the returned value of malloc. Believe me, it is a very bad habit. When you do a cast, you tell the compiler you know what you are doing. Casting the value of malloc is a legacy from when there was no void type in C (this is so far as the middle eighties). Once upon a time, malloc() used to return a char * which normally was not the type of pointer you wanted, and you had to cast the pointer to match the one your were using. Casting malloc() return value in 2021 is not only not recommended, but it is strongly discouraged, as many errors come from having cast it (the compiler warns you when you are doing something bad, but it will not, if you cast the value, normally that is interpreted as you telling the compiler you are doing something weird on purpose, so the compiler shuts up, and doesn't say more)
Third, if you are going to extract all the tokens in a string, the first time you need to call strtok() (or his friend strtok_w) with a first parameter pointing to the start of the string, but the rest of the calls have to be done with NULL as it first parameter, or you'll be searching inside the string just returned, and not behind the first occurrence. Your problem was not about using strtok or strtok_r, as strtok_r is just a reentrant version of strtok that allows you to start a nested loop, inside the first, or to call it from different threads.
Heap memory management keeps track of base memory addresses for implementing library calls. We need to preserve those base-addresses to free/reallocate whenever necessary.
Now that you found a way to use strtok_r(), I prefer the below version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main () {
char orgStr [] = "strtok does not allow you to have 2 pointers going at once on the same string";
for (char *token, *rmdStr = orgStr; token = strtok_r (NULL, " ", &rmdStr); /* empty */) {
printf ("%s\n", token);
}
/* Original string is chopped up with NULCHAR, now unreliable */
}

C: Assign strtok token to char * Segfault

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
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:
Accept a string str or NULL and a string of delimiters characters.
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.
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.
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.

casting char * to char[]

Example:
char str[10];
gets(str);
str = (char[10]) strtok(str, " "); // type error here
Since strtok() returns a char *, I get an type error without that casting. With it I get the following:
error: cast specifies array type
What is the best to fix this code?
Oh man, be careful with that gets()! Related question
You can't assign to arrays (in other words, use them as lvalues).
char *p = "string";
char array[10];
array = p; /* invalid */
Apart from that, you're not using strtok() correctly. The pointer returned points to the subsequent token, so you might want to create a separate char pointer to store it.
You should be assigning the result of strtok into a separate char* variable. You can't assign it back into str.
You should not be assigning the reult of strtok() back to your str variable in the first place. Use a separate variable instead, eg:
char str[10];
gets(str);
char *token = strtok(str, " ");
//use token as needed...
You cannot assign anything to an array. Even this simplistic program will fail:
char *foo(void) { }
int main(int argc, char *argv[])
{
char a[1];
a = foo();
return 0;
}
As indeed it does:
$ make fail
cc fail.c -o fail
fail.c: In function ‘main’:
fail.c:7:4: error: incompatible types when assigning to type ‘char[1]’ from type ‘char *’
make: *** [fail] Error 1
Either re-define str as char *str or figure out some other way to re-write your program to not attempt to assign to an array. (What does the surrounding code look like? The code you've pasted doesn't really make sense anyway...)
You can get parameter before calling your function:
char mystr[] = "192.168.0.2";
split_ip(myster[]);
char * split_ip( char ip_address[]){
unsigned short counter = 0;
char *token;
token = strtok (ip_address,".");
while (token != '\0')
{
printf("%s\n",token);
token = strtok ('\0', ".");
}
}// end of function def

Resources