pass strings by reference in C - c

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.

Related

How do I modify the contents of a string literal without using brackets in C?

Disclaimer: this is for a homework assigment.
Say I have a string that was declared like this:
char *string1;
For part of my program, I need to set string1 equal to another string, string2. I can't use strcpy or use brackets.
This is my code so far:
int i;
for(i = 0; *(string2 + i) != '\0'; i++){
*(string1 + i) = *(string2 + i);
}
This causes a segmentation fault.
According to https://www.geeksforgeeks.org/storage-for-strings-in-c/ , this is because string1 was declared like this: char *string1 and a workaround to avoid segfaults is to use brackets. I can't use brackets, so is there any workaround that I can do?
EDIT: I am also prohibited from allocating more memory or declaring arrays. I cant use malloc(), falloc() etc.
The issue you are having is that string2 does not have memory allocated to it.
Your code is missing some details, but I'll assume it looks something like this:
#include <stdio.h>
int main()
{
char *originalStr = "Hello NewArsenic";
char *newStr;
// YMMV depending on the compiler for this line. Might print (null) for
// newStr or it might throw an error.
printf("Original: %s\nNew: %s\n", originalStr, newStr);
int i;
for (i = 0; *(originalStr + i) != '\0'; i++)
{
*(newStr + i) = *(originalStr + i);
}
printf("Original: %s\nNew: %s\n", originalStr, newStr);
return 0;
}
TL;DR Your Issue
Your issue here is that you are attempting to store some values into newStr without having the memory to do so.
Solution
Use malloc.
#include <stdio.h>
#include <stdlib.h> // malloc(size_t) is in stdlib.h
#include <string.h> // strlen(const char *) is in string.h
int main()
{
char *originalStr = "Hello NewArsenic";
// Note here that size_t is preferable to int for length.
// Generally you want to be using size_t if you are working with size/length.
// More info at https://stackoverflow.com/questions/19732319/difference-between-size-t-and-unsigned-int
size_t originalLength = strlen(originalStr);
// This is malloc's typical usage, where we are asking from the system to
// give us originalLength + 1 many chars.
// The `char` here is redundant, actually, since sizeof(char) is defined to
// be one by the C spec, but you might find it useful to see the typical
// usage of `malloc`.
// Since malloc returns a void *, we need to cast that to a char *.
char *newStr = (char *)malloc((originalLength + 1) * sizeof(char));
// Your code stays the same.
printf("Original: %s\nNew: %s\n", originalStr, newStr);
size_t i;
for (i = 0; *(originalStr + i) != '\0'; i++)
{
*(newStr + i) = *(originalStr + i);
}
// Don't forget to append a null character like I did before editing!
*(newStr + originalLength) = 0;
printf("Original: %s\nNew: %s\n", originalStr, newStr);
// Because `malloc` gives us memory on the stack, we need to tell the system
// that we want to free it before exiting.
free(newStr);
return 0;
}
The long answer
What is a C String?
In C, a string is merely an array of characters. What this means is that for each character you want to have have, you need to allocate memory.
Memory
In C, there are two types of memory allocation - stack- and heap-based.
Stack Memory
You're probably more familiar with stack-based memory than you think. Whenever you declare a variable, you're defining it on the stack. Arrays declared with bracket notation type array[size_t] are stack-based too. What's specific about stack-based memory allocation is that when you allocate memory, it will only last for as long as the function in which it was declared, as you're probably familiar with. This means that you don't have to worry about your memory sticking around for longer than it should.
Heap Memory
Now heap-based memory allocation is different in the sense that it will persist until it is cleared. This is advantageous in one way:
You can keep values of which you don't know the size at compile time.
But, that comes at a cost:
The heap is slower
You have to manually clear your memory once you're done with it.
For more info, check out this thread.
We typically use the function (void *) malloc(size_t) and its sister (void *) calloc(size_t, size_t) for allocating heap memory. To free the memory that we asked for from the system, use free(void *).
Alternatives
You could've also used newStr = originalStr, but that would not actually copy the string, but only make newStr point to originalStr, which I'm sure you're aware of.
Other remarks
Generally, it's an anti-pattern to do:
char* string = "literal";
This is an anti-pattern because literals cannot be edited and shouldn't be. Do:
char const* string = "literal";
See this thread for more info.
Avoid using int in your loop. Use size_t See this thread.
For part of my program, I need to set string1 equal to another string, string2. I can't use strcpy or use brackets.
Perhaps the solution is just as simple as
string2 = string1
Note that this assignes the string2 pointer to point directly to the same memory as string1. This is sometimes very helpful because you need to maintain the beginning of the string with string1 but also need another pointer to move inside the string with things like string2++.
One way or another, you have to point string2 at an address in memory that you have access to. There are two ways to do this:
Point at memory that you already have access to through another variable either with another pointer variable or with the address-of & operator.
Allocate memory with malloc() or related functions.

(C Programming) Making a char ** array like argv

In a program I am writing I made a Tokenize struct that says:
TokenizerT *Tokenize(TokenizerT *str) {
TokenizerT *tok;
*tok->array = malloc(sizeof(TokenizerT));
char * arr = malloc(sizeof(50));
const char *s = str->input_strng;
int i = 0;
char *ds = malloc(strlen(s) + 1);
strcpy(ds, s);
*tok->array[i] = strtok(ds, " ");
while(*tok->array[i]) {
*tok->array[++i] = strtok(NULL, " ");
}
free(ds);
return tok;
}
where TokenizeT is defined as:
struct TokenizerT_ {
char * input_strng;
int count;
char **array[];
};
So what I am trying to do is create smaller tokens out of a large token that I already created. I had issues returning an array so I made array part of the TokenizerT struct so I can access it by doing tok->array. I am getting no errors when I build the program, but when I try to print the tokens I get issues.
TokenizerT *ans;
TokenizerT *a = Tokenize(tkstr);
char ** ab = a->array;
ans = TKCreate(ab[0]);
printf("%s", ans->input_strng);
TKCreate works because I use it to print argv but when i try to print ab it does not work. I figured it would be like argv so work as well. If someone can help me it would be greatl appreciated. Thank you.
Creating the Tokenizer
I'm going to go out on a limb, and guess that the intent of:
TokenizerT *tok;
*tok->array = malloc(sizeof(TokenizerT));
char * arr = malloc(sizeof(50));
was to dynamically allocate a single TokenizerT with the capacity to contain 49 strings and a NULL endmarker. arr is not used anywhere in the code, and tok is never given a value; it seems to make more sense if the values are each shifted one statement up, and corrected:
// Note: I use 'sizeof *tok' instead of naming the type because that's
// my style; it allows me to easily change the type of the variable
// being assigned to. I leave out the parentheses because
// that makes sure that I don't provide a type.
// Not everyone likes this convention, but it has worked pretty
// well for me over the years. If you prefer, you could just as
// well use sizeof(TokenizerT).
TokenizerT *tok = malloc(sizeof *tok);
// (See the third section of the answer for why this is not *tok->array)
tok->array = malloc(50 * sizeof *tok->array);
(tok->array is not a great name. I would have used tok->argv since you are apparently trying to produce an argument vector, and that's the conventional name for one. In that case, tok->count would probably be tok->argc, but I don't know what your intention for that member is since you never use it.)
Filling in the argument vector
strtok will overwrite (some) bytes in the character string it is given, so it is entirely correct to create a copy (here ds), and your code to do so is correct. But note that all of the pointers returned by strtok are pointers to character in the copy. So when you call free(ds), you free the storage occupied by all of those tokens, which means that your new freshly-created TokenizerT, which you are just about to return to an unsuspecting caller, is full of dangling pointers. So that will never do; you need to avoid freeing those strings until the argument vector is no longer needed.
But that leads to another problem: how will the string be freed? You don't save the value of ds, and it is possible that the first token returned by strtok does not start at the beginning of ds. (That will happen if the first character in the string is a space character.) And if you don't have a pointer to the very beginning of the allocated storage, you cannot free the storage.
The TokenizerT struct
char is a character (usually a byte). char* is a pointer to a character, which is usually (but not necessarily) a pointer to the beginning of a NUL-terminated string. char** is a pointer to a character pointer, which is usually (but not necessarily) the first character pointer in an array of character pointers.
So what is char** array[]? (Note the trailing []). "Obviously", it's an array of unspecified length of char**. Because the length of the array is not specified, it is an "incomplete type". Using an incomplete array type as the last element in a struct is allowed by modern C, but it requires you to know what you're doing. If you use sizeof(TokenizerT), you'll end up with the size of the struct without the incomplete type; that is, as though the size of the array had been 0 (although that's technically illegal).
At any rate, that wasn't what you wanted. What you wanted was a simple char**, which is the type of an argument vector. (It's not the same as char*[] but both of those pointers can be indexed by an integer i to return the ith string in the vector, so it's probably good enough.)
That's not all that's wrong with this code, but it's a good start at fixing it. Good luck.

C strings by reference voodoo

I am a novice programmer and I hagly appreciate any advice with my problem here.
I've made a procedure that gets a string in buffer and parses it in three cunks, separated by the first 2 ";".
What I tried to do is to pass 3 char pointers in where I will store my parsed string. But all I got in the calling function is memory garbage. What am I doing wrong?
void parseomensaje(char buf[256], char **idNodo, char **idMensaje, char **mensaje){
char *temp;
temp=(char *)malloc(sizeof(buf));
strcpy(temp, buf);
printf("\ntemp adentro de la funcion = %s\n", temp);
idNodo = strtok (temp,";");
idMensaje = strtok (NULL, ";");
mensaje = strtok (NULL, "\0");
printf("\nADENTRO\nidNodo: %s\nidMensaje: %s\nmensaje: %s", idNodo, idMensaje, mensaje);
}
this function is called this way:
char *idnod=NULL;
char *idmen=NULL;
char *men=NULL;
idnod=(char *)malloc(sizeof(buffer));
idmen=(char *)malloc(sizeof(buffer));
men=(char *)malloc(sizeof(buffer));
parseomensaje (buffer, &idmen, &idnod, &men);
after parseomensaje is executed buffer contains its original string, but idmen, idnod and men are blank.
I was reading from tutorials that pointers names are pointers itself, so it is the same thing as passing a parameter by reference, but in case of a string I need to pass the pointer address to a pointer to pointer...
I was reading it from here, but I'm still trying to figure it out.
PD: I apologize for my English, please feel free to point any mistakes in my writing. :)
This is incorrect:
char *temp;
temp=(char *)malloc(sizeof(buf));
as the array will degrade to a char* within the function, so only sizeof(char*) bytes are being allocated (typically 4 or 8 bytes). If the actually length of buf is greater than 4 or 8 bytes then the program has undefined behaviour as the strcpy() will be writing beyond the bounds of the array. Basically:
void parseomensaje(char buf[256], char **idNodo, char **idMensaje, char **mensaje){
is equivalent to:
void parseomensaje(char* buf, char **idNodo, char **idMensaje, char **mensaje){
If you are, as you say, a novice programmer I would recommend avoiding dynamic memory allocation until you get more comfortable with the language. Modify the program to use fixed sized arrays instead of dynamically allocated memory (and prevent writing beyond the bounds of the arrays). Once you have that working and fully understand it then attempt to use dynamically allocated memory.
First of all you have multiple memory leaks in your program. Consider freeing every single memory chunk you've allocated thanks to malloc once you don't need them anymore.
As regard your function:
void parseomensaje(char buf[256], char **idNodo, char **idMensaje, char **mensaje)
Why do you pass char** pointers on your function? Pass char* instead. Because strtok is declared this way:
char *strtok(char *str, const char *delim);
Moreover, you don't need to allocate any memory before calling parseomensaje since strtok returns a pointer on your own memory, not new allocated one.

Am I passing a copy of my char array, or a pointer?

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.

Confusion between passing and modifying char pointers in C (reference vs. value)

I was wondering if you could help me out with a C string problem I don't quite understand. I have a function to which I send 3 char pointers. Within this function, the char pointers are shifted and modified correctly. However, when I return to the main function from which they are called, said functions are not changed. Am I passing by value be mistake? Here is an example of my code:
int main(void)
{
LPSTR path = (char*)malloc(strlen(START_PATH));
strcpy( path, START_PATH );
char* newstr = (char*)malloc(PATH_SIZE);
TrimVal(path, "*.*", newstr);
//Do Stuff
return 0;
}
void TrimVal(char* modify, char* string, char* newstr)
{
newstr[0] = '\0';
modify = strncat(newstr, modify, (strlen(modify) - strlen(string)));
return;
}
NOTE: Assume PATH_SIZE is a size value, and START_PATH is a char array
In doing this
modify = strncat(newstr, modify, (strlen(modify) - strlen(string)));
You are modifying the pointer, not what the pointer points to.
When you pass in path to TrimVal. It will pass in the memory location of path e.g. 0x12345
When you do the modify = you are saying, change the local variable modify to be a new memory location, e.g. 0x54321
When you return to main, it only has a pointer to 0x12345, and when it looks there, nothing has changed.
You can easily fix your problem by doing
{
...
TrimVal(&path, "*.*", newstr);
...
}
void TrimVal(char** modify, char* string, char* newstr)
{
newstr[0] = '\0';
*modify = strncat(newstr, *modify, (strlen(*modify) - strlen(string)));
return;
}
void TrimVal(char* modify, char* string, char* newstr)
Changing the values of modify, string, or newstr inside the TrimVal() function has no effect on the variables at the calling function.
Changing the contents of modify, string, or newstr inside the TrimVal() function will be reflected on the variables at the calling function.
So
void TrimVal(char* modify, char* string, char* newstr)
{
newstr[0] = '\0'; /* will be reflected in the calling function */
modify = "a new string"; /* won't be reflected */
}
I think your function, with a little clearing of code, could do what you want.
Oh ... and you have a memory leak with the path variable: you malloc some space for it and immediately afterwards lose the address of that space by assigning a different value to the path variable.
A couple of points in addition to the many other good ones raised in this thread:
LPSTR path = (char*)malloc(strlen(START_PATH));
If this is C, you should not cast the return value of malloc. (See C FAQ 7.7b.
More importantly, strlen does not include the terminating \0 in its calculation. So, the memory path points to is one character short of the required amount of memory to hold START_PATH plus the \0. Therefore:
strcpy(path, START_PATH);
invokes undefined behavior by writing one past the memory pointed to by path.
If you expect your char* variable to be modified in the function and you want to pass by reference, you need to pass it as char* . Remember, you are passing the pointer by reference, so there needs to be an extra layer of indirection (passing char does pass something by reference - a single character!)
I see a problem with the first two statements. You are declaring path as a pointer char and allocating memory for it that is stored in this address holder. In your next statement, you are changing the value in path, pointing it to the start of your char array, START_PATH. The memory you allocated is now lost.
Also, strncat does not call malloc to concatenate. It is expected that you are passing in a buffer large enough to hold the concat, and this is a potential security risk (buffer overrun).
Just one comment about your style of casting the return type of the malloc call. When casting this can hide errors.
This would be a much better style.
Include the stdlib.h and try and make the call to malloc as type independent.
char *ptr_char = NULL;
ptr_char = malloc(sizeof(*ptr_char));
Hope this helps,
C doesn't really have a pass-by-reference. What you are doing here is passing pointers by value. A string in C is represented by a pointer to char. So in the function TrimVal you can modify the contents of the string (that is, the pointed-to data), but not the pointer itself.
strncat modifies the contents of the first parameter and returns the same value.
If you want to change the value of path within TrimVal, you should pass a pointer to a pointer, like so:
...
TrimVal(path, "*.*", newstr);
...
void TrimVal(char** modify, char* string, char* newstr)
{
newstr[0] = '\0';
*modify = strncat(newstr, *modify, (strlen(*modify) - strlen(string)));
return;
}

Resources