Pass strings by reference in C

I find it difficult to understand how to pass strings back through function parameters. I am new to programming, so I think this is probably a beginner. Any help you could give would be greatly appreciated. This seg code is faulty and I'm not sure why, but I am providing my code to show what I still have.

I made this community a wiki, so feel free to edit.

PS 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 is an updated version with some of the suggested suggestions:

 #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); } 
+7
c string pass-by-reference
source share
8 answers

Firstly, these mallocs must be for strlen(whatever)+1 bytes. Lines C have a 0 character indicating the end, called the NUL terminator, and it is not included in the length measured by strlen.

Next, strtok modifies the string you are looking for. You give it a pointer to a string that you are not allowed to change (you cannot change literals). This may be the reason for segfault. Therefore, instead of using a pointer to an unmodifiable string literal, you can copy it to your modifiable buffer, for example:

 char mybaz[] = "hello:world"; 

In this case, a 12 char cluster is pushed onto the stack and copies the bytes of the string literal to this array. It works because the compiler knows at compile time how long the string is, and can make space accordingly. This saves the use of malloc for this particular copy.

The problem you are referring to is that you are passing the value of mybaz, myfoo and mybar to your function. You cannot change the variables of the caller unless you pass a pointer to myfoo and mybar. Since myfoo is char *, the pointer to it is 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 

Changing foo in your code function has absolutely no effect on myfoo . myfoo not initialized, so if none of the first two things call it, segfault most likely occurs when you get to print using this uninitialized pointer.

Once you basically work it out, you may need to add error handling. strtok can return NULL if it does not find the separator that it is looking for, and you cannot call strlen with NULL. malloc can return NULL if there is not enough memory, and you also cannot call strcpy with NULL.

+7
source share

One thing that everyone doesn't notice is that you call strtok in an array stored in const memory. strtok writes to the array that you are passing, so make sure you copy it to a temporary array before calling strtok on it or just allocating the source code, for example:

 char mybaz[] = "hello:world"; 
+2
source share

In C, you usually pass by reference, passing 1) the pointer to the first element of the array and 2) the length of the array.

The length of the array can sometimes be skipped if you are confident in the size of the buffer and know the length of the string by looking for a character with a null terminating character (a character with a value of 0 or '\0' .

As you can see from the code example, you are trying to set the value that the pointer points to. Therefore, you probably need a char** pointer. And you will pass the address of your char* variable that you want to set.

+1
source share

You want to pass 2 pointers. Therefore, you need to call it with a pair of pointers to pointers. Something like that:

 void fn(char *baz, char **foo, char **bar) { ... *foo = malloc( ... ); ... *bar = malloc( ... ); ... } 
+1
source share

Oh yes, there are few problems.

Generally, if you intend to manipulate strings inside a function, the storage for these strings should be better than the function. An easy way to achieve this is to declare arrays outside the function (for example, in main() ) and pass arrays (which automatically become pointers to their beginning) to the function. This works fine until the result lines overflow the space allocated in the arrays.

You went a more flexible, but a bit more complicated route: you use malloc() to create space for your results (so far!), And then try to assign the space malloc'd to the pointers you are passing. This, alas, will not work.

Pointer entry - 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 points to.

If you succeeded, great. If not, please specify.

+1
source share

the code is most likely segfaults, because you allocate space for the string, but forget that the string has an extra byte at the end, a null terminator.

Also you pass only a pointer. Since the pointer is a 32-bit value (on a 32-bit machine), you simply pass the value of the unified pointer to "fn". Similarly, you won’t select the integer passed to the function that will be returned to the calling function (without explicitly returning it), you cannot expect the pointer to do the same. Thus, new pointer values ​​never return to the main function. You usually do this by passing a pointer to a pointer in C.

Also do not 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 ); } 
0
source share

Other answers describe how to fix your answer to the job, but a simple way to accomplish what you mean is strdup (), which allocates new memory of the appropriate size and copies the correct characters to.

However, you need to fix the business with char * vs char **. There simply is no way.

0
source share

The significant problem is that although storage is always allocated (with malloc() ) for the results you are trying to return as myfoo and mybar , pointers to these distributions do not actually return to main() . As a result, a later call to printf() will most likely reset the kernel.

The solution is to declare the arguments as a ponter for the char pointer and pass the addresses from 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); } 

Remember to free each highlighted line later, otherwise you will create memory leaks.

To make both malloc () and strcpy () in one call, it would be better to use strdup() , as it also remembers to allocate space for the ending NUL that you left outside your code, as written. *foo = strdup(pch) much clearer and easier to maintain this alternative. Since strdup() is POSIX, not ANSI C, you may need to implement it yourself, but the effort will be well paid out as a result of clarity for such use.

Another traditional way to return a string from function C is for the caller to allocate storage and indicate its function address. This is the method used, for example, by sprintf() . He suffers from the problem that there is no way to make such a call site completely safe for buffer overflow errors caused by the called function, assuming that more space has been allocated than is actually available. The traditional repair of this problem is to require that the buffer length argument also be passed, and carefully check both the actual distribution and the length declared on the call site in the code review.

Edit:

The actual segfault that you get will most likely be inside strtok() , not printf() , because your sample, as it is written, is trying to pass a string constant to strtok() , which should be able to modify the string. This is officially Undefined Behavior.

The fix for this problem is to make sure that bybaz declared as an initialized array, and not as a pointer to a char . The initialized array will be in writable memory, and the string constant is likely to be in read-only memory. In many cases, string constants are stored in the same part of memory that is used to store the executable code itself, and modern systems try to make it difficult to modify a program with its own code.

In the embedded systems that I work on for a living, the code will most likely be stored in some kind of ROM and cannot be physically modified.

0
source share

All Articles