I'm having trouble figuring out how to pass strings back through the parameters of a function. I'm new to programming, so I imagine this this probably a beginner question. Any help you could give would be most appreciated. This code seg faults, and I'm not sure why, but I'm providing my code to show what I have so far. I have made this a community wiki, so feel free to edit. P.S. This is not homework. This is the original version
#include #include #include void fn(char *baz, char *foo, char *bar) < char *pch; /* this is the part I'm having trouble with */ pch = strtok (baz, ":"); foo = malloc(strlen(pch)); strcpy(foo, pch); pch = strtok (NULL, ":"); bar = malloc(strlen(pch)); strcpy(bar, pch); return; >int main(void)UPDATE Here's an updated version with some of the suggestions implemented:
#include #include #include #define MAXLINE 1024 void fn(char *baz, char **foo, char **bar) < char line[MAXLINE]; char *pch; strcpy(line, baz); pch = strtok (line, ":"); *foo = (char *)malloc(strlen(pch)+1); (*foo)[strlen(pch)] = '\n'; strcpy(*foo, pch); pch = strtok (NULL, ":"); *bar = (char *)malloc(strlen(pch)+1); (*bar)[strlen(pch)] = '\n'; strcpy(*bar, pch); return; >int main(void)community wiki for your strtok segfault look at my suggestion below Commented Dec 7, 2009 at 22:05
First thing, those mallocs should be for strlen(whatever)+1 bytes. C strings have a 0 character to indicate the end, called the NUL terminator, and it isn't included in the length measured by strlen.
Next thing, strtok modifies the string you're searching. You are passing it a pointer to a string which you're not allowed to modify (you can't modify literal strings). That could be the cause of the segfault. So instead of using a pointer to the non-modifiable string literal, you could copy it to your own, modifiable buffer, like this:
char mybaz[] = "hello:world";
What this does is put a size 12 char array on the stack, and copy the bytes of the string literal into that array. It works because the compiler knows, at compile time, how long the string is, and can make space accordingly. This saves using malloc for that particular copy.
The problem you have with references is that you're currently passing the value of mybaz, myfoo, and mybar into your function. You can't modify the caller's variables unless you pass a pointer to myfoo and mybar. Since myfoo is a char*, a pointer to it is a char**:
void fn(char *baz, char **foo, char **bar) // take pointers-to-pointers *foo = malloc(. ); // set the value pointed to by foo fn(mybaz, &myfoo, &mybar); // pass pointers to myfoo and mybar
Modifying foo in the function in your code has absolutely no effect on myfoo . myfoo is uninitialised, so if neither of the first two things is causing it, the segfault is most likely occurring when you come to print using that uninitialised pointer.
Once you've got it basically working, you might want to add some error-handling. strtok can return NULL if it doesn't find the separator it's looking for, and you can't call strlen with NULL. malloc can return NULL if there isn't enough memory, and you can't call strcpy with NULL either.
community wikiThe seg fault seems to be happening on pch = strtok (baz, ":"); actually. I'm still trying to figure out why
Commented Dec 7, 2009 at 21:56Yes, sorry. I only noticed my "second thing" after the first couple of versions of my answer. Hopefully I've explained that.
Commented Dec 7, 2009 at 21:59One thing everyone is overlooking is that you're calling strtok on an array stored in const memory. strtok writes to the array you pass it so make sure you copy that to a temporary array before calling strtok on it or just allocate the original one like:
char mybaz[] = "hello:world";
community wiki
"Overlooked?". I got there eventually ;-)
Commented Dec 7, 2009 at 22:05
Together, we are the Uber-mind! No question can withstand the crowdsource's powerful inter. what was that, intneglect? Well, whatever it's called.
Commented Dec 7, 2009 at 22:10 @Steve: LOL! I meant it just as a tackon. There were a few things wrong as everyone's noted :) Commented Dec 7, 2009 at 22:12Ooh yes, little problem there.
As a rule, if you're going to be manipulating strings from inside a function, the storage for those strings had better be outside the function. The easy way to achieve this is to declare arrays outside the function (e.g. in main() ) and to pass the arrays (which automatically become pointers to their beginnings) to the function. This works fine as long as your result strings don't overflow the space allocated in the arrays.
You've gone the more versatile but slightly more difficult route: You use malloc() to create space for your results (good so far!) and then try to assign the malloc'd space to the pointers you pass in. That, alas, will not work.
The pointer coming in is a value; you cannot change it. The solution is to pass a pointer to a pointer, and use it inside the function to change what the pointer is pointing to.
If you got that, great. If not, please ask for more clarification.
community wikiDon't call them references, you'll just get confused when you dive into C++. C has pointers. Arrays decay to pointers.
Commented Dec 8, 2009 at 3:57In C you typically pass by reference by passing 1) a pointer of the first element of the array, and 2) the length of the array.
The length of the array can be ommitted sometimes if you are sure about your buffer size, and one would know the length of the string by looking for a null terminated character (A character with the value of 0 or '\0' .
It seems from your code example though that you are trying to set the value of what a pointer points to. So you probably want a char** pointer. And you would pass in the address of your char* variable(s) that you want to set.
community wikiYou're wanting to pass back 2 pointers. So you need to call it with a pair of pointers to pointers. Something like this:
void fn(char *baz, char **foo, char **bar)community wiki Boo on your casting of malloc() ! BOO I SAY! Commented Dec 8, 2009 at 3:53
You know, I'd done that out of habit for so long, I'd ceased to think about it. Thanks for the nudge to revisit that bit of knowledge.
Commented Dec 8, 2009 at 15:44the code most likely segfaults because you are allocating space for the string but forgetting that a string has an extra byte on the end, the null terminator.
Also you are only passing a pointer in. Since a pointer is a 32-bit value (on a 32-bit machine) you are simply passing the value of the unitialised pointer into "fn". In the same way you wouldn't expact an integer passed into a function to be returned to the calling function (without explicitly returning it) you can't expect a pointer to do the same. So the new pointer values are never returned back to the main function. Usually you do this by passing a pointer to a pointer in C.
Also don't forget to free dynamically allocated memory!!
void fn(char *baz, char **foo, char **bar) < char *pch; /* this is the part I'm having trouble with */ pch = strtok (baz, ":"); *foo = malloc(strlen(pch) + 1); strcpy(*foo, pch); pch = strtok (NULL, ":"); *bar = malloc(strlen(pch) + 1); strcpy(*bar, pch); return; >int main(void)community wiki
Other answers describe how to fix your answer to work, but an easy way to accomplish what you mean to do is strdup(), which allocates new memory of the appropriate size and copies the correct characters in.
Still need to fix the business with char* vs char**, though. There's just no way around that.
community wikiThe essential problem is that although storage is ever allocated (with malloc() ) for the results you are trying to return as myfoo and mybar , the pointers to those allocations are not actually returned to main() . As a result, the later call to printf() is quite likely to dump core.
The solution is to declare the arguments as ponter to pointer to char , and pass the addresses of myfoo and mybar to fn . Something like this (untested) should do the trick:
void fn(char *baz, char **foo, char **bar) < char *pch; /* this is the part I'm having trouble with */ pch = strtok (baz, ":"); *foo = malloc(strlen(pch)+1); /* include space for NUL termination */ strcpy(*foo, pch); pch = strtok (NULL, ":"); *bar = malloc(strlen(pch)+1); /* include space for NUL termination */ strcpy(*bar, pch); return; >int main(void)
Don't forget the free each allocated string at some later point or you will create memory leaks.
To do both the malloc() and strcpy() in one call, it would be better to use strdup() , as it also remembers to allocate room for the terminating NUL which you left out of your code as written. *foo = strdup(pch) is much clearer and easier to maintain that the alternative. Since strdup() is POSIX and not ANSI C, you might need to implement it yourself, but the effort is well repaid by the resulting clarity for this kind of usage.
The other traditional way to return a string from a C function is for the caller to allocate the storage and provide its address to the function. This is the technique used by sprintf() , for example. It suffers from the problem that there is no way to make such a call site completely safe against buffer overrun bugs caused by the called function assuming more space has been allocated than is actually available. The traditional repair for this problem is to require that a buffer length argument also be passed, and to carefully validate both the actual allocation and the length claimed at the call site in code review.
Edit:
The actual segfault you are getting is likely to be inside strtok() , not printf() because your sample as written is attempting to pass a string constant to strtok() which must be able to modify the string. This is officially Undefined Behavior.
The fix for this issue is to make sure that bybaz is declared as an initialized array, and not as a pointer to char . The initialized array will be located in writable memory, while the string constant is likely to be located in read-only memory. In many cases, string constants are stored in the same part of memory used to hold the executable code itself, and modern systems all try to make it difficult for a program to modify its own running code.
In the embedded systems I work on for a living, the code is likely to be stored in a ROM of some sort, and cannot be physically modified.