I am trying to study C, and I am running in to troubles using char* and char arrays. I am using a generic hash-set container from a library (which I don't want to describe in details). This library includes the function
void *HashSetLookup(hashset *h, const void *elemAddr);
which I have to use to search in the hash set to see if the element already exists there (the hash and compare functions are part of the hashset struct). In this case I use the hashset to store pointers to C-strings, or more specifically (char * *) . My problem is that the following code gives a segmentation fault:
char word[1024];
/* Some code that writes to the word buffer */
HashSetLookup(stopList, &word);
while this code works fine (and as expected):
char word[1024];
/* The same code as before that writes to the word buffer */
char* tmp = strdup(word);
HashSetLookup(stopList, &tmp);
free(tmp);
I thought char word[] and char* were basically the same thing. The only difference being that char word[1024] is in the stack with a fixed length of 1024, but tmp in the heap occupying only as much space as necessary (strlen(word)+1).
Therefore I don't understand why I have to make a copy of the string in the heap to be able to call this function. Why does this happen? Is there some more fundamental difference between char* tmp = strdup("something") and char word[1024] = "something"?
You mention you need a char ** and there lies the problem: for an array, word and &word mean the same thing - the actual location of the array contents. The reason it works when you use a pointer is because the "pointer" is stored at a different location, while it points to the same array. You don't need an strdup, you simply need to create a pointer:
char* tmp = word;
HashSetLookup(stopList, &tmp);
Hard to tell without the documentation of HashSetLookup.
But it expects a const void * as its second parameter, so you should pass tmp, and not &tmp, because tmp is already a pointer.
I don't see need for char ** here at all.
Also, you might probably be interested in what HashSetLookup() returns.
You must have missed something in the first example because "word" is not being used at all.
Anyhow, in most of the environments, when you write 'char *s = "Hello World"', it gets created on the code segment and cannot be modified. Code segment means that it is part of the executable code which must not be modified but only read. When you try to write it, you get a seg fault.
However 'char[]' gets created on data segment so it can be modified without any problem.
Perhaps this helps:
http://www.iso-9899.info/wiki/Why_arrays_arent_pointers
http://www.lysator.liu.se/c/c-faq/c-2.html
Since you mentioned char** I think the problem is with the function trying to write to the location pointed by the second argument, i.e. when you write:
HashSetLookup( stopList, &word );
it goes and tries assigning an address to word (and that's why it needs the address of it.), which overwrites the buffer with a pointer.
This is demonstrated by the following silly snippet (keep in mind that address of array is still the address of its first element):
#include <stdio.h>
#include <stdlib.h>
void func( void* boo )
{
char** ptr = ( char** )boo;
printf( "func: got %p\n", boo );
*ptr = "bad func";
}
int main( int argc, char* argv[] )
{
char buf[128], *p;
func( &buf ); /* buf got overwritten */
printf( "array: %s\n", buf );
p = malloc( 128 );
func( &p ); /* p got new value */
printf( "malloc: %s\n", p );
return 0;
}
Related
I have been trying and searching online for too long without any success. I've tried a lot of the suggested answers but nothing has worked for me.
I want to basically send in a char*. It can make it NULL if necessary, but would rather the function modify a char* that already has something.
Example:
char *old = "kit";
function(old){ //does stuff and writes "kat" to old}
printf("new = %s", old);
And
result: new = kat
How can I do this?
Thanks
EDIT:
What I'm currently trying:
calling_function(char *in){
char **old = NULL;
function(&old);
printf("old in calling function is now = %s", *old);
}
function(**old){
<does stuff to get char *another_string = "kat">
*old = another_string;
printf("old is now = %s ", *old);
}
And the result is:
old is now "kat"
old in calling function is now =
and it immediately exist the system with an unspecified error exit(-1) then hangs.
A char* is nothing more an address that points to some bytes which are then interpreted as a string, how to do what you need really depends on what you need to do.
If you want to change a character of the string then a normal char* (non const) pointer will be enough:
void function(char *data) {
data[0] = 'a';
}
If, instead, you want to replace the whole string with another one (possibly of different length), then you will need to pass the address that contains the address, so that you can directly replace it to a new address (that points to a different string):
void function(char **data) {
*data = strdup("newstring");
// strdup is used because a string literal must be considered as const
// otherwise you could invoke UB by modifying the returned string
}
char *value;
function(&value);
An example for passing integer as reference is here: Passing by reference in C
For your example, the value can be changed in the function as below:
char *old = "kit";
/* this will print kit */
printf("old = %s",old);
function(old);
/* this will print kat */
printf("updated old = %s", old);
function(char *old) {
*old = "kat"
}
The line
char *old = "kit";
Can cause trouble because old may point to read-only memory. What you want to do is this:
char old[128]; // or however many you need
function(old){ //does stuff and writes "kat" to old // You can use sprintf for this}
printf("new = %s", old);
Which will allocate old on the stack, where it can be modified.
This will take an existing char * and change it.
char* old = "kit";
void changingFunction( char* pointer ) {
strcpy( pointer, "kat" );
/* or just pointer[1] = 'a'; */
}
changingFunction(old);
printf("new = %s\n", old);
Be careful, though. Remember that you're essentially dealing with an array, and the function doesn't know the size of the array. You always want to stay within the bounds of the array.
Consequently, you should make the function more advanced:
void changingFunction( char* pointer ) {
char * newString = "kat";
strncpy(pointer, newString, strlen(pointer));
}
By using strncpy, you ensure that you stay within your bounds, and since you're dealing with a null terminated char*, you can use strlen to find how big your bounds are.
You can change your function prototype to
void myFunction(char** pold)
and within that function, you are free to write
*pold = "kat";
And call the function like this: myFunction(&old);
But beware; this approach has dangers:
1) You may leak memory as the previous string (i.e. what was old originally pointing to?) may be dangling.
2) *pold = "kat"; assigns read-only memory to *pold. This is because "kat" is probably added to a string literal pool on program startup by the C runtime library. Attempting to modify the string (e.g. (*pold)[0] = 'K') is undefined behaviour. Using *pold = strdup("kat"); circumvents this problem.
I learned something by trying to answer this question! Here's my program that does the operation:
#include <stdio.h>
void f(char* str)
{
strcpy(str, "kat");
}
int main(void) {
// char* str = "kit"; // Initializing like this causes a crash!
char str[4]; // This initialization works
strcpy(str, "kit");
f(str);
printf(str); // Prints "kat"
return 0;
}
There are obvious issues with safety, but what was strange to me is that if you declare str using the commented-out line, the program crashes. I didn't realize that initializing a string literal like that gave you a pointer to read-only memory. That's very non-obvious to me, so I was confused when my little program crashed.
I think it's important to point out that fact first and foremost because it's central to why a naive solution to the problem wouldn't necessarily work. Interesting.
im trying to copy a const char array to some place in the memory and point to it .
lets say im defining this var under the main prog :
char *p = NULL;
and sending it to a function with a string :
myFunc(&p, "Hello");
now i want that at the end of this function the pointer will point to the letter H but if i puts() it, it will print Hello .
here is what i tried to do :
void myFunc(char** ptr , const char strng[] ) {
*ptr=(char *) malloc(sizeof(strng));
char * tmp=*ptr;
int i=0;
while (1) {
*ptr[i]=strng[i];
if (strng[i]=='\0') break;
i++;
}
*ptr=tmp;
}
i know its a rubbish now, but i would like to understand how to do it right, my idea was to allocate the needed memory, copy a char and move forward with the pointer, etc..
also i tried to make the ptr argument byreferenec (like &ptr) but with no success due to a problem with the lvalue and rvalue .
the only thing is changeable for me is the function, and i would like not to use strings, but chars as this is and exercise .
thanks for any help in advance.
Just replace all the char* with std::string. Do that until you have a very specific reason to not use existing utilities, which is something you don't have as a beginner. None of the code above requires malloc() or raw pointers.
Some more notes:
const char strng[] as parameter is the same as const char* strng. The array syntax doesn't make it an array, it remains a pointer. I don't use this syntax in order to avoid this confusion.
Use static_cast or one of the other C++ casts not the C-style like (char*)malloc(..). The reason is that they are safer.
Check the return value of malloc(), it can return null. Also, you must call free() eventually, otherwise your application leaks memory.
Finally, the pointer does point to the 'H', which is just the first element of the string. Output *p instead of p to see this.
You code work as desired except
*ptr[i]=strng[i];
should be
(*ptr)[i]=strng[i];
Without the parens, it acts like `*(ptr[i])=strng[i];
2) Also
malloc(sizeof(strng));
s/b
malloc(strlen(strng)+1);
You may want to look at strdup(strng).
[edit per OP's request]
difference between *(ptr[i]) and (*ptr)[i]
// OP desired function
(*ptr)[i] = 'x';
// Dereference ptr, getting the address of a char *array.
// Assign 'x' to the i'th element of that char * array.
// OP post with the undesired function
*(ptr[i]) = 'x';
// This is like
char *q = ptr[i];
*q = 'x';
// This make sense _if_ ptr were an array of char *.
// Get the i'th char * from ptr and assign to q.
// Assign 'x' to to the location pointer to by q.
This is all the code needed...
nothing more...
void myFunc(char **pp, char * str){
*pp = str;
}
The only issue here is that "Hello" resides in read only section because it is a constant string... so you can't change "Hello" to something else...
I am a beginner in C. I wanted to make strcat function using pointers. I made it but don't know what is wrong with it. I used gcc compiler and it gave segmentation fault output.
#include<stdio.h>
#include<string.h>
char scat(char *,char *);
void main()
{
char *s="james";
char *t="bond";
char *q=scat(s,t);
while(*q!='\0') printf("the concatenated string is %c",*q);
}
char *scat(char *s,char *t)
{
char *p=s;
while(*p!='\0'){
p++;
}
while(*t!='\0'){
*p=*t;
p++;
t++;
}
return p-s-t;
}
This one works:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *scat(char *,char *); /* 1: your prototype was wrong */
void main()
{
char *s="james";
char *t="bond";
char *q=scat(s,t);
printf("cat: %s\n", q); /* 2: you can use %s to print a string */
free(q);
}
char *scat(char *s,char *t)
{
char *p=malloc(strlen(s)+strlen(t)+1); /* 3: you will have to reserve memory to hold the copy. */
int ptr =0, temp = 0; /* 4 initialise some helpers */
while(s[temp]!='\0'){ /* 5. use the temp to "walk" over string 1 */
p[ptr++] = s[temp++];
}
temp=0;
while(t[temp]!='\0'){ /* and string two */
p[ptr++]=t[temp++];
}
return p;
}
You have to allocate new space to copy at the end of s. Otherwise, your while loo[ will go in memory you don't have access to.
You shoul learn about malloc() here.
It is undefined behaviour to modify a string literal and s, and eventually p, is pointing to a string literal:
char* s = "james";
s is passed as first argument to scat() to which the local char* p is assigned and then:
*p=*t;
which on first invocation is attempting to overwite the null character an the end of the string literal "james".
A possible solution would be to use malloc() to allocate a buffer large enough to contain the concatentation of the two input strings:
char* result = malloc(strlen(s) + strlen(p) + 1); /* + 1 for null terminator. */
and copy them into it. The caller must remember to free() the returned char*.
You may find the list of frequently asked pointer questions useful.
Because p goes till the end of the string and then it starts advancing to illegal memory.
That is why you get segmentation fault.
It's because s points to "james\0", string literal & you cannot modify constant.
Change char *s="james"; to char s[50]="james";.
You need to understand the basics of pointers.
a char * is not a string or array of characters, it's the address of the beginning of the data.
you can't do a char * - char* !!
This is a good tutorial to start with
you will have to use malloc
You get a segmentation fault because you move the pointer to the end of s and then just start writing the data of p to the memory directly following s. What makes you believe there is writable memory available after s? Any attempt to write data to non-writable memory results in a segmentation fault and it looks like the memory following s is not writable (which is to expect, since "string constants" are usually stored in read-only memory).
Several things look out of order.
First keep in mind that when you want to return a pointer to something created within a function it needs to have been malloc'ed somewhere. Much easier if you pass the destination as an argument to the function. If you follow the former approach, don't forget to free() it when you're done with it.
Also, the function scat has to return a pointer in the declaration i.e. char *scat, not char scat.
Finally you don't need that loop to print the string, printf("%s", string); will take care of printing the string for you (provided it's terminated).
At first, your code will be in infinte loop because of the below line. you were supposed to use curely braces by including "p++; t++ " statements.
while(*t!='\0')
*p=*t;
though you do like this, you are trying to alter the content of the string literal. which will result in undefined behavior like segmentation fault.
A sequence of characters enclosed with in double quotes are called as string literal. it is also called as "string". String is fixed in size. once you created, you can't extend its size and alter the contents. Doing so will lead to undefined behavior.
To solve this problem , you need to allocate a new character array whose size is sum of the length of two strings passed. then append the two strings into the new array. finally return the address of the new array.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* scat(char *,char *);
void append(char *t , char *s);
int main(void)
{
char *s="james";
char *t="bond";
char *n = scat(s,t);
printf("the concatenated string is %s",n);
return 0;
}
char* scat(char *s,char *t)
{
int len = strlen(s) + strlen(t);
char *tmp = (char *)malloc(sizeof(char)* len);
append(tmp,s);
append(tmp,t);
return tmp;
}
void append(char *t , char *s)
{
//move pointer t to end of the string it points.
while(*t != '\0'){
t++;
}
while( *s != '\0' ){
*t = *s;
t++;
s++;
}
}
I've been studying C, and I decided to practice using my knowledge by creating some functions to manipulate strings. I wrote a string reverser function, and a main function that asks for user input, sends it through stringreverse(), and prints the results.
Basically I just want to understand how my function works. When I call it with 'tempstr' as the first param, is that to be understood as the address of the first element in the array? Basically like saying &tempstr[0], right?
I guess answering this question would tell me: Would there be any difference if I assigned a char* pointer to my tempstr array and then sent that to stringreverse() as the first param, versus how I'm doing it now? I want to know whether I'm sending a duplicate of the array tempstr, or a memory address.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char* stringreverse(char* tempstr, char* returnptr);
printf("\nEnter a string:\n\t");
char tempstr[1024];
gets(tempstr);
char *revstr = stringreverse(tempstr, revstr); //Assigns revstr the address of the first character of the reversed string.
printf("\nReversed string:\n"
"\t%s\n", revstr);
main();
return 0;
}
char* stringreverse(char* tempstr, char* returnptr)
{
char revstr[1024] = {0};
int i, j = 0;
for (i = strlen(tempstr) - 1; i >= 0; i--, j++)
{
revstr[j] = tempstr[i]; //string reverse algorithm
}
returnptr = &revstr[0];
return returnptr;
}
Thanks for your time. Any other critiques would be helpful . . only a few weeks into programming :P
EDIT: Thanks to all the answers, I figured it out. Here's my solution for anyone wondering:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void stringreverse(char* s);
int main(void)
{
printf("\nEnter a string:\n\t");
char userinput[1024] = {0}; //Need to learn how to use malloc() xD
gets(userinput);
stringreverse(userinput);
printf("\nReversed string:\n"
"\t%s\n", userinput);
main();
return 0;
}
void stringreverse(char* s)
{
int i, j = 0;
char scopy[1024]; //Update to dynamic buffer
strcpy(scopy, s);
for (i = strlen(s) - 1; i >= 0; i--, j++)
{
*(s + j) = scopy[i];
}
}
First, a detail:
int main()
{
char* stringreverse(char* tempstr, char* returnptr);
That prototype should go outside main(), like this:
char* stringreverse(char* tempstr, char* returnptr);
int main()
{
As to your main question: the variable tempstr is a char*, i.e. the address of a character. If you use C's index notation, like tempstr[i], that's essentially the same as *(tempstr + i). The same is true of revstr, except that in that case you're returning the address of a block of memory that's about to be clobbered when the array it points to goes out of scope. You've got the right idea in passing in the address of some memory into which to write the reversed string, but you're not actually copying the data into the memory pointed to by that block. Also, the line:
returnptr = &revstr[0];
Doesn't do what you think. You can't assign a new pointer to returnptr; if you really want to modify returnptr, you'll need to pass in its address, so the parameter would be specified char** returnptr. But don't do that: instead, create a block in your main() that will receive the reversed string, and pass its address in the returnptr parameter. Then, use that block rather than the temporary one you're using now in stringreverse().
Basically I just want to understand how my function works.
One problem you have is that you are using revstr without initializing it or allocating memory for it. This is undefined behavior since you are writing into memory doesn't belong to you. It may appear to work, but in fact what you have is a bug and can produce unexpected results at any time.
When I call it with 'tempstr' as the first param, is that to be understood as the address of the first element in the array? Basically like saying &tempstr[0], right?
Yes. When arrays are passed as arguments to a function, they are treated as regular pointers, pointing to the first element in the array. There is no difference if you assigned &temp[0] to a char* before passing it to stringreverser, because that's what the compiler is doing for you anyway.
The only time you will see a difference between arrays and pointers being passed to functions is in C++ when you start learning about templates and template specialization. But this question is C, so I just thought I'd throw that out there.
When I call it with 'tempstr' as the first param, is that to be understood as the
address of the first element in the array? Basically like saying &tempstr[0],
right?
char tempstr[1024];
tempstr is an array of characters. When passed tempstr to a function, it decays to a pointer pointing to first element of tempstr. So, its basically same as sending &tempstr[0].
Would there be any difference if I assigned a char* pointer to my tempstr array and then sent that to stringreverse() as the first param, versus how I'm doing it now?
No difference. You might do -
char* pointer = tempstr ; // And can pass pointer
char *revstr = stringreverse(tempstr, revstr);
First right side expression's is evaluavated and the return value is assigned to revstr. But what is revstr that is being passed. Program should allocate memory for it.
char revstr[1024] ;
char *retValue = stringreverse(tempstr, revstr) ;
// ^^^^^^ changed to be different.
Now, when passing tempstr and revstr, they decayed to pointers pointing to their respective first indexes. In that case why this would go wrong -
revstr = stringreverse(tempstr, revstr) ;
Just because arrays are not pointers. char* is different from char[]. Hope it helps !
In response to your question about whether the thing passed to the function is an array or a pointer, the relevant part of the C99 standard (6.3.2.1/3) states:
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
So yes, other than the introduction of another explicit variable, the following two lines are equivalent:
char x[] = "abc"; fn (x);
char x[] = "abc"; char *px = &(x[0]); fn (px);
As to a critique, I'd like to raise the following.
While legal, I find it incongruous to have function prototypes (such as stringreverse) anywhere other than at file level. In fact, I tend to order my functions so that they're not usually necessary, making one less place where you have to change it, should the arguments or return type need to be changed. That would entail, in this case, placing stringreverse before main.
Don't ever use gets in a real program.. It's unprotectable against buffer overflows. At a minimum, use fgets which can be protected, or use a decent input function such as the one found here.
You cannot create a local variable within stringreverse and pass back the address of it. That's undefined behaviour. Once that function returns, that variable is gone and you're most likely pointing to whatever happens to replace it on the stack the next time you call a function.
There's no need to pass in the revstr variable either. If it were a pointer with backing memory (i.e., had space allocated for it), that would be fine but then there would be no need to return it. In that case you would allocate both in the caller:
char tempstr[1024];
char revstr[1024];
stringreverse (tempstr, revstr); // Note no return value needed
// since you're manipulating revstr directly.
You should also try to avoid magic numbers like 1024. Better to have lines like:
#define BUFFSZ 1024
char tempstr[BUFFSZ];
so that you only need to change it in one place if you ever need a new value (that becomes particularly important if you have lots of 1024 numbers with different meanings - global search and replace will be your enemy in that case rather than your friend).
In order to make you function more adaptable, you may want to consider allowing it to handle any length. You can do that by passing both buffers in, or by using malloc to dynamically allocate a buffer for you, something like:
char *reversestring (char *src) {
char *dst = malloc (strlen (src) + 1);
if (dst != NULL) {
// copy characters in reverse order.
}
return dst;
}
This puts the responsibility for freeing that memory on the caller but that's a well-worn way of doing things.
You should probably use one of the two canonical forms for main:
int main (int argc, char *argv[]);
int main (void);
It's also a particularly bad idea to call main from anywhere. While that may look like a nifty way to get an infinite loop, it almost certainly will end up chewing up your stack space :-)
All in all, this is probably the function I'd initially write. It allows the user to populate their own buffer if they want, or to specify they don't have one, in which case one will be created for them:
char *revstr (char *src, char *dst) {
// Cache size in case compiler not smart enough to do so.
// Then create destination buffer if none provided.
size_t sz = strlen (src);
if (dst == NULL) dst = malloc (sz + 1);
// Assuming buffer available, copy string.
if (dst != NULL) {
// Run dst end to start, null terminator first.
dst += sz; *dst = '\0';
// Copy character by character until null terminator in src.
// We end up with dst set to original correct value.
while (*src != '\0')
*--dst = *src++;
}
// Return reversed string (possibly NULL if malloc failed).
return dst;
}
In your stringreverse() function, you are returning the address of a local variable (revstr). This is undefined behaviour and is very bad. Your program may appear to work right now, but it will suddenly fail sometime in the future for reasons that are not obvious.
You have two general choices:
Have stringreverse() allocate memory for the returned string, and leave it up to the caller to free it.
Have the caller preallocate space for the returned string, and tell stringreverse() where it is and how big it is.
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
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)
{
char *mybaz, *myfoo, *mybar;
mybaz = "hello:world";
fn(mybaz, myfoo, mybar);
fprintf(stderr, "%s %s", myfoo, mybar);
}
UPDATE Here's an updated version with some of the suggestions implemented:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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)
{
char *mybaz, *myfoo, *mybar;
mybaz = "hello:world";
fn(mybaz, &myfoo, &mybar);
fprintf(stderr, "%s %s", myfoo, mybar);
free(myfoo);
free(mybar);
}
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.
One 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";
Ooh 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.
In 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.
You'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) {
...
*foo = malloc( ... );
...
*bar = malloc( ... );
...
}
the 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)
{
char *mybaz, *myfoo, *mybar;
mybaz = "hello:world";
fn(mybaz, &myfoo, &mybar);
fprintf(stderr, "%s %s", myfoo, mybar);
free( myFoo );
free( myBar );
}
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.
The 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)
{
char mybaz[] = "hello:world";
char *myfoo, *mybar;
fn(mybaz, &myfoo, &mybar);
fprintf(stderr, "%s %s", myfoo, mybar);
free(myfoo);
free(mybar);
}
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.