I'm a C noob and I'm having problems with the following code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void split_string(char *conf, char *host_ip[]){
long unsigned int conf_len = sizeof(conf);
char line[50];
strcpy(line, conf);
int i = 0;
char* token;
char* rest = line;
while ((token = strtok_r(rest, "_", &rest))){
host_ip[i] = token;
printf("-----------\n");
printf("token: %s\n", token);
i=i+1;
}
}
int main(){
char *my_conf[1];
my_conf[0] = "conf01_192.168.10.1";
char *host_ip[2];
split_string(my_conf[0], host_ip);
printf("%s\n",host_ip[0]);
printf("%s\n",host_ip[1]);
}
I want to modify the host_ip array inside the split_string function and then print the 2 resulting strings in the main.
However, the 2 last printf() are only printing unknown/random characters (maybe an address?). Any help?
There are 2 problems:
First, you're returning pointers to local variables. You can avoid this by strduping the strings and freeing in the caller.
Second:
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.
I.e. you must NULL for the first argument after the first iteration in the loop. Nowhere is it said that it is OK to use the same pointer for both arguments.
This is because the strtok_r is an almost drop-in replacement to the braindead strtok, with just one extra argument, so that you could even wrap it with a macro...
Thus we get
char *start = rest;
while ((token = strtok_r(start, "_", &rest))){
host_ip[i] = strdup(token);
printf("-----------\n");
printf("token: %s\n", token);
i++;
start = NULL;
}
and in the caller:
free(host_ip[0]);
free(host_ip[1]);
You are storing address of local variable (line) which is in stack.Stack is LIFO and has valid data for local variables in its stack memory during its function life time.after that, the same stack memory will be allocated to another function's local variables. So, data stores in line【50】's memory will be invalid after coming out of string_split function
Related
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 */
}
I want to outsource a string operation to a function and then ask the results in main. This doesn't work, and I don't understand why.
#include <stdio.h>
#include <string.h>
void splitRequest(char request[], char method[], char ressource[], char proto[]) {
method = strtok(request, " ");
ressource = strtok(NULL, " ");
proto = strtok(NULL, " ");
printf("\nResult:\n\nmethod:\t\t%s\nressource:\t%s\nproto:\t\t%s\n",method,ressource,proto);
}
int main()
{
char method[50], ressource[50], proto[50], request[50];
memset(method, '\0', 50);
memset(ressource, '\0', 50);
memset(proto, '\0', 50);
memset(request, '\0', 50);
strcpy(request,"Get /index.htm HTTP/1.1");
//rehash query
splitRequest(request, method, ressource, proto);
//check Results
printf("\nResult:\n\nmethod:\t\t%s\nressource:\t%s\nproto:\t\t%s\n",method,ressource,proto);
return 0;
}
In the splitRequest function all the arguments are pointers. And more importantly they are local variables. So all changes to them (like making them point anywhere else) will only affect the local variable, nothing else.
The solution is to copy to the memory instead. Perhaps something like
char *temp;
if ((temp = strtok(request, " ")) != NULL)
{
strcpy(method, temp);
}
// And so on...
A little elaboration about what I mean...
In C all function arguments are passed by value. That means their value is copied and the function only have a local copy of the value. Chaning the copy will of course not change the original value.
It is the same with pointers. When you call your splitRequest function the pointers you pass are copied. Inside the function the variable method (for example) is pointing to the memory of the array you defined in the main function. When you assign to this variable, like you do with
method = strtok(...);
you only modify the local variable, the local copy of the pointer. When the function returns the local variable method goes out of scope, and all changes to it are lost.
There are two solutions to this problem. One is to emulate pass by reference (something which C doesn't have, which is why it must be emulated), but that will not work as long as you have an array. Therefore the second and easiest solution: To copy to the memory pointed to by the local variable method, which is what I show above.
An important note though: When you call the splitRequest function, passing the arrays, the arrays themselves are not passed. Instead the arrays decays to a pointer to their first element, and inside the function the variables defined by the arguments are pointers and not arrays.
The call
splitRequest(request, method, ressource, proto);
is equal to
splitRequest(&request[0], &method[0], &ressource[0], &proto[0]);
And the function declaration
void splitRequest(char request[], char method[], char ressource[], char proto[])
is equal to
void splitRequest(char *request, char *method, char *ressource, char *proto)
So no the strings are not copied, only the pointers. Otherwise you would have gotten an error when assigning to the pointer, since you can't assign to arrays only copy to them.
Found another workaround even more effectiv:
#include <stdio.h>
#include <string.h>
void splitRequest(char request[], char **method, char **ressource, char **proto) {
*method = strtok(request, " ");
*ressource = strtok(NULL, " ");
*proto = strtok(NULL, " ");
printf("\nResult:\n\nmethod:\t\t%s\nressource:\t%s\nproto:\t\t%s\n",*method,*ressource,*proto);
}
int main()
{
char *method, *ressource, *proto, request[50];
memset(request, '\0', 50);
strcpy(request,"Get /index.htm HTTP/1.1");
//rehash query
splitRequest(&request, &method, &ressource, &proto);
//check Results
printf("\nResult:\n\nmethod:\t\t%s\nressource:\t%s\nproto:\t\t%s\n",method,ressource,proto);
return 0;
}
I'm trying to split the input from fgets using strtok, and store the results in an array, i.e. newArgs, so I can then call execvp and essentially execute the input passed by fgets.
E.g. ls -la will map to /bin/ls -la and execute correctly.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
char buff[1024];
fgets(buff, 1024, stdin);
buff[strcspn(buff, "\n")] = 0;
printf("%s\n", buff);
printf("%d\n", strlen(buff));
char *newArgs[30];
char *token;
char delim[2] = " ";
token = strtok(buff, delim);
int i = 0;
while(token != NULL)
{
if(newArgs[i])
{
sprintf(newArgs[i], "%s", token);
printf("%s\n", newArgs[i]);
}
token = strtok(NULL, delim);
i++;
}
execvp(newArgs[0], newArgs);
return 0;
}
I keep getting a Segmentation fault, even though I'm checking the existence of newArgs[i], which is a little odd. Any ideas as to what's going wrong?
You're not allocating any memory for each element of newArgs. Try using a multi-dimensional array, like newArgs[30][100]. Don't forget to ensure they're null terminated.
Problems I see:
You are using uninitialized values of newArgs[i]. You have:
char *newArgs[30];
This is an array of uninitialized pointers. Then, you go on to use them as:
if(newArgs[i])
That is cause for undefined behavior. You can fix that by initializing the pointers to NULL.
char *newArgs[30] = {};
You haven't allocated memory for newArgs[i] before calling
sprintf(newArgs[i], "%s", token);
That is also cause for undefined behavior. You can fix that by using:
newArgs[i] = strdup(token);
The list of arguments being passed to execvp must contains a NULL pointer.
From http://linux.die.net/man/3/execvp (emphasis mine):
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer.
You are missing the last requirement. You need o make sure that one of the elements of newArgs is a NULL pointer. This problem will go away if you initialize the pointers to NULL.
You are not allocating memory for newArgs before storing it in the string.
Add
newArgs[i] = malloc(strlen(token));
before the if statement inside the for loop.
There is absolutely no reason to copy the tokens you are finding in buff.
That won't always be the case, but it certainly is here: buff is not modified before the execvp and execvp doesn't return. Knowing when not to copy a C string is not as useful as knowing how to copy a C string, but both are important.
Not copying the strings will simplify the code considerably. All you need to do is fill in the array of strings which you will pass to execvp:
char* args[30]; /* Think about dynamic allocation instead */
char** arg = &args[0];
*arg = strtok(buff, " ");
while (*arg++) {
/* Should check for overflow of the args array */
*arg = strtok(NULL, " ");
}
execvp(args[0], args);
Note that the above code will store the NULL returned by strtok at the end of the args array. This is required by execvp, which needs to know where the last arg is.
So I'm new to C and the whole string manipulation thing, but I can't seem to get strtok() to work. It seems everywhere everyone has the same template for strtok being:
char* tok = strtok(source,delim);
do
{
{code}
tok=strtok(NULL,delim);
}while(tok!=NULL);
So I try to do this with the delimiter being the space key, and it seems that strtok() no only reads NULL after the first run (the first entry into the while/do-while) no matter how big the string, but it also seems to wreck the source, turning the source string into the same thing as tok.
Here is a snippet of my code:
char* str;
scanf("%ms",&str);
char* copy = malloc(sizeof(str));
strcpy(copy,str);
char* tok = strtok(copy," ");
if(strcasecmp(tok,"insert"))
{
printf(str);
printf(copy);
printf(tok);
}
Then, here is some output for the input "insert a b c d e f g"
aaabbbcccdddeeefffggg
"Insert" seems to disappear completely, which I think is the fault of strcasecmp(). Also, I would like to note that I realize strcasecmp() seems to all-lower-case my source string, and I do not mind. Anyhoo, input "insert insert insert" yields absolutely nothing in output. It's as if those functions just eat up the word "insert" no matter how many times it is present. I may* end up just using some of the C functions that read the string char by char but I would like to avoid this if possible. Thanks a million guys, i appreciate the help.
With the second snippet of code you have five problems: The first is that your format for the scanf function is non-standard, what's the 'm' supposed to do? (See e.g. here for a good reference of the standard function.)
The second problem is that you use the address-of operator on a pointer, which means that you pass a pointer to a pointer to a char (e.g. char**) to the scanf function. As you know, the scanf function want its arguments as pointers, but since strings (either in pointer to character form, or array form) already are pointer you don't have to use the address-of operator for string arguments.
The third problem, once you fix the previous problem, is that the pointer str is uninitialized. You have to remember that uninitialized local variables are truly uninitialized, and their values are indeterminate. In reality, it means that their values will be seemingly random. So str will point to some "random" memory.
The fourth problem is with the malloc call, where you use the sizeof operator on a pointer. This will return the size of the pointer and not what it points to.
The fifth problem, is that when you do strtok on the pointer copy the contents of the memory pointed to by copy is uninitialized. You allocate memory for it (typically 4 or 8 bytes depending on you're on a 32 or 64 bit platform, see the fourth problem) but you never initialize it.
So, five problems in only four lines of code. That's pretty good! ;)
It looks like you're trying to print space delimited tokens following the word "insert" 3 times. Does this do what you want?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char str[BUFSIZ] = {0};
char *copy;
char *tok;
int i;
// safely read a string and chop off any trailing newline
if(fgets(str, sizeof(str), stdin)) {
int n = strlen(str);
if(n && str[n-1] == '\n')
str[n-1] = '\0';
}
// copy the string so we can trash it with strtok
copy = strdup(str);
// look for the first space-delimited token
tok = strtok(copy, " ");
// check that we found a token and that it is equal to "insert"
if(tok && strcasecmp(tok, "insert") == 0) {
// iterate over all remaining space-delimited tokens
while((tok = strtok(NULL, " "))) {
// print the token 3 times
for(i = 0; i < 3; i++) {
fputs(tok, stdout);
}
}
putchar('\n');
}
free(copy);
return 0;
}
I have written a program in which in the main function I declare an array of pointers and then I call a function which splits a given sentence and then want to assign it to the array of pointers in main(). I am unable to do. Can you please check the code pasted below:
int main(void)
{
char *data[3];
allocate(data);
/* Unable to print the strings here */
printf("Main is %s\n", data[0] );
printf(""
}
void allocate(char **dt)
{
int i;
char buf[] = "The great Scorpion";
char delims[] = " ";
size_t len;
char *p;
char *result = NULL;
result = strtok(buf," ");
*dt = result;
int j = 1;
while(result!=NULL)
{
result = strtok( NULL, delims );
dt[j]=result;
j++;
}
/* able to print values here */
printf( "result is %s\n", dt[0]);
printf( "result is %s\n", dt[1] );
printf( "result is %s\n", dt[2] );
}
Can anyone please help me out?
strtok does not allocate new strings, it returns a pointer to an existing string (and substitutes delimiters with null characters in place). So in allocate, you fill dt with pointers into buf. Since buf is an automatic variable, its lifetime ends when allocate returns, and all pointers in dt are invalidated.
If I remember correctly, strtok() doesn't do dynamic allocation, it actually modifies the string that you pass to in the first time you call it. So, in this case, it modifies buf. So dt is an array of pointers into buf. And when you exit the function, buf is destroyed.
Actually you might just add static to your buf declaration.
Ok,
You have an allocate function to which you pass the return pointer array.
Inside the function you allocate a string on the stack, and,
Make the strtok return pointers to this stack area outside the function
At that point you have 'dangling' pointers to the string -- effectively
Then you call printf which kill the data in unallocated stack
you miss the strings in printf.
If you want to do this, the correct way would be to
really allocate strings in your allocate function and
then free them from main after you are done with them.
The older way to work with strtok was to use strdup in the allocate and free later.
OldStuff...
I think you need to pass the &data to your allocate function.
Either that, or i am not all awake yet.
As a small aside (that won't actually have an effect on your program), you loop is structured wrong.
int j = 1;
while(result!=NULL)
{
result = strtok( NULL, delims );
dt[j]=result;
j++;
}
You set dt[0], dt[1], and dt[2], correctly. However due the the nature of your loop (you check, call strtok, then insert into dt,) you're also assigning NULL into dt[3]. This would probably run fine in this trivial example, but you're probably trashing your stack. You should really structure your loop like
result = strtok(buf," ");
int j=0
while(result != NULL)
{
dt[j]=result;
j++;
result = strtok(NULL, delims);
}
You're trying to return the address of a local, non-static variable; once the allocate() function exits, the buf array no longer exists, so the pointers in your data array are no longer pointing to anything meaningful.
What you need to do is save a copy of the token, rather than just a pointer to it:
void allocate(char **dt)
{
char buf[] = "The Great Scorpion";
char delim[] = " ";
size_t i = 0;
char *result = strtok(buf, delim);
while (result)
{
dt[i] = malloc(strlen(result) + 1);
if (dt[i])
{
strcpy(dt[i++], result);
}
result = strtok(NULL, delim);
}
}
int main(void)
{
char *data[3];
allocate(data);
...
}
Alternately, you can define data to be a 2D array of char, rather than just an array of pointers, but you need to make sure it's sized to handle the maximum string length; also, the type passed to allocate changes:
#define STRING_SIZE ... /* large enough for longest string */
void allocate(char (*dt)[STRING_SIZE+1])
{
char buf[] = "The Great Scorpion";
char delim[] = " ";
size_t i = 0;
char *result = strtok(buf, delim);
while (result)
{
strcpy(dt[i++], result);
result = strtok(NULL, delim);
}
}
int main(void)
{
char data[3][STRING_SIZE];
allocate(data);
...
}
Dynamically allocating memory is more flexible, but requires some bookkeeping and you have to remember to free it when you're done with it.
To not trash the stack, the while loop should be guarded with the size of the passed parameter "char *data[3]" which is 3 in this case, i.e extra parameter.
But i agree adding static to "static char buf[] = ...." is the quickest answer to get out of pointing to the de-allocated stack of a terminated function.
You haven't allocated the memory to be used inside data[X], just allocated data.
data[0] == null pointer