main(){
char *cmd1[20] = {NULL};
int x = parse_command(cmd1);
printf("%s\ ",cmd1[0]);
}
parse_command(char *inTempString){
char tempString[256];
(call to function that assigns a string to tempString)
cmd1[0] = tempString;
}
There is a problem when printing out the cmd1[0] within main. I am pretty sure that it is a dangling pointer error. I don't really know how to go about fixing it.
There are two issues with your program.
First, when you say:
char *cmd1[20] = {NULL};
cmd1 is an array of 20 pointers to char. This means that cmd1[i] for i in [0,20) is a pointer to char.
There is a rule in C that says that passing an array to a function only passes the pointer to the first element of the array to the function. I.e., if you had code like:
int ai[20];
f(ai);
then the type of ai in the function call f(ai); is int * and the pointer passed to f() is equal to &ai[0], the first element of ai.
So, when you do:
parse_command(cmd1);
you immediately know that the "thing" passed to parse_command() is &cmd1[0], i.e., a pointer to the first element of cmd1. Since cmd1[0] is of type char *, you are passing a char ** to parse_command. Therefore, your declaration:
parse_command(char *inTempString);
is wrong, you should do:
parse_command(char **inTempString);
to match your call. This assumes that parse_command() will parse more than one value in cmd1. If that is the case, you should also pass the number of elements in cmd1 to parse_commnd()—since it can't know how many elements cmd1 has.
Your second problem is that you can't return the address of a local variable from a function in C. As above, in addition to a function call, returning an array in C, or assigning something to an array in C also makes the name of an array "decay" to a pointer to its first element.
So given your function:
/* changed inTempString to cmd1 because that's what you probably meant */
int parse_command(char *cmd1)
{
char tempString[256];
/* call to function that assigns a string to tempString */
cmd1[0] = tempString;
/* you need to return an int from here */
}
the tempString in the assignment to cmd1[0] is actually &tempString[0], i.e., a pointer to the first element of tempString. But since tempString is destroyed as soon as the function returns, the pointer becomes invalid. You can't use the value later.
In fact, in C, the name of an array decays to a pointer to its first element in all cases, except:
when used as an operand to sizeof operator, and
when used as an operand to the address-of (&) operator
To be more precise, in object contexts, the name of an array doesn't decay to a pointer, and in value contexts, it decays to a pointer. See this for more details.
Now, how should you fix your second issue? It depends—you can either allocate memory dynamically in parse_command(), and assign that memory to cmd1[0], or you can make tempString static in the function. Since static variables in a function are not destroyed when a function returns, you can continue using a pointer to it. Dynamic allocation is more work—you need to worry about allocation failure and you need to remember to free the pointer when done. static array is easier, but you have to be careful because another call to parse_command will overwrite the array, making it less-generic.
Assuming you want to go the "dynamic memory" route, here is a scheme that you could use:
#include <stdio.h> /* printf */
#include <stdlib.h> /* malloc and free */
int main(void) /* main returns int */
{
char *cmd1[20] = {NULL};
/* number of commands. "sizeof cmd1" is the number of bytes
used by the cmd1 array, and "sizeof cmd1[0]" is the number
of bytes used by one element of the array. The division
gives you the number of elements. This is 20 of course
but doing it this way makes sure that changing "20" to any
number works. */
size_t ncmds = sizeof cmd1 / sizeof cmd1[0];
/* pass the number of commands to "parse_command", since
it can't know otherwise */
int x = parse_command(cmd1, ncmds);
int i;
for (i=0; i < x; ++i) {
printf("%s ", cmd1[i]);
free(cmd1[i]);
}
return 0; /* return a value from main */
}
int parse_command(char **cmd1, size_t ncmds)
{
char *tempString; /* we will malloc this */
int i; /* the number of mallocs done successfully */
tempString = malloc(...);
if (tempString == NULL) {
/* failure, handle gracefully */
} else {
++i; /* make sure i doesn't exceed or equal ncmds */
}
cmd1[0] = tempString;
/* do the above as many times as you need */
return i; /* the number successfully assigned to */
}
You're declaring cmd1 in main as a char** -- that is, a pointer to pointer to char. However, you then pass it to parse_command, which you've defined as taking a char*; a pointer to char.
This only compiles because of automatic conversion of pointer-to-anything to pointer-to-char. That's a historical artifact of old versions of C that used 'char*' in place of 'void*'; in your case, it just means that the compiler is ignoring the type error that you made rather than reporting it to you.
Something like this will work:
parse_command(char **inTempString){
static char tempString[256];
strcpy(tempString,"some string you want to copy");
inTempString[0] = tempString;
}
In your code the tempString would not
exist once the function returns. You
need to keep it alive even after the
function returns. You can allocate
the space dynamically and de-allocate
later or you can declare it as
static.
Also you need to change the type
argument inTempString from char* to
char**.
You are trying to access cmd1 variable that is inside main function from parse_command.
I would say that at least the cmd1[0] will look like an integer because it is not declared withing that method.
The cmd1 is declared as array of char* but the parameter to method is char* which might be a pointer to char array but not pointer to array of char*.
The best way to copy char array into another char array is to use either memcpy,strcpy or similar methods that accept pointers to src, dest and size to be copied.
Yeah, you can't do that.
char tempString[256];
declares a variable on the stack in the function parse_command, that variable goes out of scope and pointers to it cease to be valid when parse_command returns.
You need to allocate the command string on the heap, so that it will still be valid when parse_command returns. This is one way.
parse_command(char *inTempString){
char tempString[256];
(call to function that assigns a string to tempString)
int cb = strlen(tempString)+1;
cmd1[0] = (char *)malloc(cb);
strcpy(cmd1[0], tempString);
}
You should also call free(cmd[0]) before main exits.
In addition to that, this code doesn't compile. You can't reference cmd1[0] from inside the parse_command function. You should be getting a type mismatch when you try and pass cmd1 into parse_command, If you mean to return a char* from parse_command then it should be declared to take a char** as an argument, more like this.
parse_command(char **pcmd){
char tempString[256];
(call to function that assigns a string to tempString)
int cb = strlen(tempString)+1;
pcmd[0] = (char *)malloc(cb);
strcpy(pcmd[0], tempString);
}
Related
I'm a newbie to C. I had extended the question from the previous question: Strange behavior when returning "string" with C (Thanks for all who answered or commented that question, by the way.)
Pretty straight forward:
Why can this work:
#include <stdio.h>
int func() {
int i = 5;
return i;
}
int main() {
printf("%d",func());
}
But not this:
#include <stdio.h>
char * func() {
char c[] = "Hey there!";
return c;
}
int main() {
printf("%s",func());
}
From the previous question, logically the int i should not exist too because the function has returned, but why can it still be returned while char c[] cannot?
(It seems to be duplicated from "Pointers and memory scope" but I would like to know more about what is the difference between returning an int and a char *.)
Problem is not returning char *, it is returning something that is allocated on stack.
If you allocate memory for your string rather than pointing to function stack, there will be no problem. Something like this:
char * func() {
char c[] = "Hey there!";
return strdup(c);
}
int main() {
char* str = func();
printf("%s", str);
free(str);
}
It is important to mention that in both cases, you are copying a value and in both cases copied value is correct, but the meaning of copied value differs.
In first case, your are copying an int value and after your return from function, you are using that int value which will be valid. But in 2nd case, even though you have a valid pointer value, it refers to an invalid address of memory which is stack of called function.
Based on suggestions in comment, I decided to add another better practice in memory allocating for this code:
#define NULL (void*)0
int func(char *buf, int len) {
char c[] = "Hey there!";
int size = strlen(c) + 1;
if (len >= size) {
strcpy(buf, c);
}
return size;
}
int main() {
int size = func(NULL, 0);
char *buf = calloc(size, sizeof(*buf));
func(buf, size);
printf("%s", buf);
free(buf);
return 0;
}
Similar approach is used in a lot of windows API functions. This approach is better, because owner of pointer is more obvious (main in here).
In the first example the return value is copied. In your second example you're returning a pointer, which will point to a memory location which no longer exists.
In the first case, you return the int value 5 from the function. You can then print that value.
In the second case however, you return a value of type char *. That value points to an array that is local to the function func. After that function returns the array goes out of scope, so the pointer points to invalid memory.
The difference between these two cases is a value that you use directly, versus a pointer value that no longer points to valid memory. Had you returned a pointer to memory allocated by malloc, then the pointer would point to valid memory.
You are trying to return pointer to local array, which is very bad. If you want to return a pointer to array, allocate it dynamically using malloc inside your func();
Then you must call free() on caller side to free up memory you allocated when you no longer need it
In the first example, you return an int, and the second you return a pointer to a char. They both return in exactly the same manner, it is just a matter of understanding the stack and how values are returned.
Even though i was declared in the function and is allocated on the stack, when the function returns it returns the value of i (which is basically copied, so when i falls off the stack the value of i is still returned.)
This is the exact same thing that happens to the char * in the second example. It will still be a pointer to a char, and it returns the 'copied' value of c. However, since it was allocated on the stack, the address it points to is effectively invalid. The pointer value itself has not changed, but what it points to has.
You would have to dynamically allocate this to avoid this situation.
The return value of function is returned by copy. In the first example, you get a copy of the integer variable from the function. In the second you get a copy of the char pointer, not a copy of the string.
The pointer references the string data that has automatic storage, so is no longer valid after the function returns. The space becomes available for use by other code and many be modified - any attempt to access it has undefined behaviour.
The point is, it is a pointer that is returned, not a string; in C a strings (and more generally arrays) are not a first-class data types.
Depending on your needs there are a number of valid ways of returning the string data; for example the following is valid:
char* func()
{
static char c[] = "Hey there!";
return c;
}
because here although the local variable goes out of scope the static data is not destroyed or de-allocated, and any reference to it remains valid.
Another alternative is to embed the string in a struct which is a first-class data type:
typedef struct
{
char content[256] ;
} sString ;
sString func()
{
sString c = {"Hey there!"};
return c;
}
Or more conventionally to copy the data to a caller buffer:
char* func( char* buffer )
{
char c[] = "Hey there!";
strcpy( buffer, c ) ;
return buffer ;
}
I have omitted code to mitigate the possibility of buffer overrun above for clarity in this last example, such code is advised.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have this code:
char** SplitToWords(char* str);
int main()
{
char** wordarr;
char str[] = "This is a sentence";
wordarr = SplitToWords(str);
return 0;
}
After the main comes the function implementation.
I am not sure the following does what I want it to do (i.e. receive an array of strings from a function):
wordarr = SplitToWords(str);
I somehow managed to convince the compiler that it's ok, but I assume it just does something else.
If it does, how do I find out the length of the array (the number of strings in it).
Thanks
I'll try to quickly visit all aspects you might not yet fully understand:
A string in C is described as a contiguous sequence of chars, ending with a char of value 0 (as a literal: '\0'). It is not a first class object, therefore hasn't its own type. So what you use to hold a string is an array of char. Therefore, taking your question by the word, "receive an array of strings from a function" is not possible.
An array is a contiguous sequence of objects of the same type. In C, the identifier of an array doesn't have a value itself; when it's evaluated, it decays as a pointer to the array's first element instead. This is especially important when passing arrays to functions or returning them from functions -- you can't actually pass the array, you always pass a pointer
e.g. you could write:
char x[] = "foo"; // initialize a char array from a string literal
char *xp = x; // here, x evaluates as a pointer to the first element of the array
You already use pointer types for your function's argument and return value, I just think it's quite important to understand what happens entirely.
You write char** SplitToWords(char* str); and ask whether this returns an "array of strings" -- well, sort of, as you should understand after reading 1. and 2. -- What it does is returning a pointer to char *. This pointer could be a pointer to the first element of an array. So in this case, it would return a pointer to an array of char * pointers. Each of these pointers could itself be a pointer to an array of chars, therefore point to a string. But what's very important is to understand you never return an array, you always return a pointer to it. It's so important because:
You might get the idea to do something like this:
char** SplitToWords(char* str)
{
char *words[16];
// code to fill `words` with pointers to the actual words
return words; // WRONG!
}
Here, because you're not returning the array words but a pointer to it (see point 2), you return a pointer to an object that no longer exists. words is in the scope of your function and has automatic storage duration, that means it only lives as long as the execution is inside of the function. One solution would be to declare words with the static storage class specifier. This way, it lives for the entire execution time of the program. But be aware that this also means there's only a single instance ever, it's always the same object. This will be a major headache for threaded programs, for example. The other way around is to dynamically allocate words using malloc(). But then, the caller of the function must free() it later.
As for your second question, how to let the caller know the number of words -- it's in the comments already, but just for completeness, a typical approach to solve this is to append another entry that is a NULL pointer. So the caller can iterate over the pointers until it finds NULL.
Regarding your comment, of course you can create the array outside the function and pass a pointer to the function, so the function only fills it. This is a common idiom in C (e.g. think about fgets(), which takes a pointer to the char array that's filled with a string by the function).
Functions working this way will need an additional size_t parameter, so they know the size of the array they should fill through the pointer, otherwise you'd have the risk of buffer overflows (this is why gets() was finally removed from the C standard). If you decide that the caller provides the storage, your function should have this prototype:
// returns the number of words found, up to `nwords`
size_t SplitToTwords(char **words, size_t nwords, char *str);
It should be called e.g. like this:
char *words[16];
size_t nwords = SplitToWords(words, 16, "the quick brown fox"); // returns 4
Remember that the strings holding the words themselves need storage as well. You can either manipulate the bytes in str to insert a '\0' after each word, overwriting the first whitespace character (this is what strtok() does) or you can copy the words to new strings, but then you would have to malloc() each of them again and the caller has to free() them later.
Yes, you could solve it by using a function with return value char **. However, there's no way to find out how many words there are afterwards.
You can solve this by allocating one more element for the return pointer and set it to NULL. Then you can get the number of words with this code:
wordarr = SplitToWords(str);
char **ptr=wordarr;
int noWords=0;
while(!*(ptr+noWords))
noWords++;
But if you want to return multiple data in C, you either need to define a return struct or using return arguments. In this case, it could look like this for the first option:
typedef struct wordList {
char **wordarr;
int noWords;
}
wordList SplitToWords(char* str);
And the second:
char** SplitToWords(char* str, int *noWords);
or
void SplitToWords(char* str, char*** wordarr, int *noWords);
Note that there's three *. That's because we want it to be a pointer to char **
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXSTRINGS 5000
int main(int argc, char *argv[]) {
char *stringTable[MAXSTRINGS];
char sentence[] = "This is a sentence";
char *token = NULL;
int i = 0;
while ((token = strtok(token == NULL ? sentence : NULL, " ")) != NULL)
{
printf("%s\n\r", token);
stringTable[i] = (char *)malloc(strlen(token) + 1); //have no "plain" C compiler - VS C++ used so cast needed :)
strcpy(stringTable[i++], token);
}
stringTable[i] = NULL; // if you need to iterate through later
printf("%d tokens found\n\r", i);
for (int y = 0; y < i; y++)
free(stringTable[y]);
}
When pc is assigning cdefg, why it is printing abc. when it goes to fun it is assigning pc= ""cdefg"
void fun(char *pc)
{
pc = malloc(5);
pc = "cdefg";
}
int main()
{
char *p = "abc";
char *c = p;
fun(p);
printf("%s %s\n",p,c);
}
The reason your program does what it does is that the assignment of pc in fun has nothing to do with assigning p in main. The pointer is passed by value; any changes made by fun get discarded.
If you would like to assign a new value inside a function, do one of three things:
Pass a pointer to pointer, or
Allocate a buffer in the caller, and pass it to the function, along with buffer's length, or
Return the pointer from the function, and assign in the caller.
First approach:
void fun(char **ppc) {
*ppc = "cdefg";
}
...
fun(&p); // in main
Second approach:
void fun(char *pc, size_t len) {
if (len >= 6) {
strcpy(pc, "cdefg");
}
}
...
char p[20]; // in main
fun(p, 20);
Third approach:
char *fun() {
return "sdefg";
}
...
char *p = fun(); // in main
Your program has other issues - for example, malloc-ed memory gets leaked by the assignment that follows.
Try this instead. It actually updates the original pointer, rather than assigning to a copy which is then left dangling:
void fun(char **pc)
{
*pc = malloc(6);
strcpy(*pc, "cdefg");
}
int main()
{
char *p = "abc";
char *c = p;
fun(&p);
printf("%s %s\n",p,c);
}
It also fixed 2 other problems. The buffer of size 5 isn't big enough for the string plus the string terminator character, and you also need to copy the string into the buffer - assignment won't work.
When the function fun is called, the value of the pointer p is copied. Thus, only the local pointer pc in fun is changed. If you want to change the value of a pointer, you should take a double pointer as argument.
By the way, you do not have to call malloc(3) because the string "cdefg" is already present in memory (in rodata). The instruction pc = "cdefg"; puts the address of "cdefg" into pc. You will loose the address of the memory allocated by malloc(3), it's a memory leak.
When you allocated the pointer again in caller function, the value of pointer variable changed. In order to take this new value to the calling function, you have to pass the address of the pointer. ie: pass the pointer by reference.
There are two things at play here, passing by value and reassigning instead of copying.
If we start with the simple reassignment, take a closer look at these two lines:
pc = malloc(5);
pc = "cdefg";
The first lines assign to pc, making pc point to whatever memory malloc returned. The second line reassigns pc to point somewhere else. These two lines are basically the same as having an int variable i and doing
i = 1;
i = 2;
The first assignment you do is lost because the you immediately make another assignment. To make the memory returned by malloc contain the string "cdefg" there are two things you need to do: The first is that you need to allocate six characters, to fit the string terminator, and the second thing you need to do is to copy the string into the memory:
pc = malloc(strlen("cdefg") + 1);
strcpy(pc, "cdefg");
The second issue is more complex, and has to do with how arguments are passed in C. In C the arguments are passed by values which means they are copied and the function only have a local copy of the data in those arguments.
When you pass a pointer, like in your code, then the pointer is copied into the variable pc, and when the function returns the variable goes out of scope and all changes you made to the variable (like reassigning it to point somewhere else) are simply lost.
The solution is to pass arguments by reference. This is unfortunately not possible in C, but it can be emulated using pointers, or rather using pointers to variables. To pass a pointer to a variable that is a pointer, the type is a pointer to a pointer to some other type, so the function should take a pointer to a pointer to char:
void fun(char **ppc) { ... }
The variable ppc points to the variable p from the main function.
Now since ppc is pointing to a variable, you need to use the dereference operator to access the original variable:
*ppc = malloc(strlen("cdefg") + 1);
strcpy(*ppc, "cdefg");
To call the function you use the address-of operator & to create a pointer to the variable:
char *p = "abc";
...
fun(&p);
Because
char *p - in main function
and
char *pc - in fun function
are different variables.
I'm attempting to run execvp using the data from a char[][] type (aka an array of strings). Now I know that execvp() takes a pointer to a string as its first parameter and then a pointer to an array of strings as its second - in fact I have even used it successfully before as such - however I cannot seem to get the correct combination of pointers & strings to get it to work out below - whatever I try is deemed incompatible!
Any help very grateful :) - I've removed my headers to compact down the code a bit!
struct userinput {
char anyargs[30][30]; //The tokenised command
};
int main() {
struct userinput input = { { { 0 } } }; //I believe is valid to set input to 0's
struct userinput *inPtr = &input; //Pointer to input (direct access will be unavailable)
strcpy(inPtr->anyargs[0], "ls"); //Hard code anyargs to arbitary values
strcpy(inPtr->anyargs[1], "-lh");
char (*arrPointer)[30]; //Pointer to an array of char *
arrPointer = &(inPtr->anyargs[0]);
printf("arrPointer[0]: %s, arrPointer[1]: %s\n", arrPointer[0],
arrPointer[1]);
printf("At exec case; ");
execvp( arrPointer[0], arrPointer);
perror("Command not recognised"); //Prints string then error message from errno
return 0;
}
There is no such thing as char[][] in C. execvp requires an array of pointers to const char. This can be written as either char * const * or char * const [].
You however have an array of 30-characters-long arrays, not an array of pointers. The two types are not compatible, not interchangeable, and not convertible one to another in either direction.
In this line
char (*arrPointer)[30]; //Pointer to an array of char *
you attempt to declare a pointer to an array of char*, incorrectly. What you have declared instead is a pointer to char[30], which is very different from what execvp expects.
The next line
arrPointer = &(inPtr->anyargs[0]);
purports to initialize a pointer to an array of char* with a pointer to char[30], which cannot possibly be correct even if you declare a pointer to an array of char*, because the right hand side of the assignment is not a pointer to an array of char*, it's a pointer to char[30] and no sequence of casts, indices, addresses and dereferences will turn one to the other.
An array of 30 pointers to char is declared like this:
char* arguments[30];
A dynamically-sized array of pointers to char is made like this:
char** arguments = calloc (nargs, sizeof(char*));
You need to use one of those if you want to call execvp.
In either case each pointer in the array of pointers must be initialized to point to an individual NUL-terminated character array (possibly to elements of your char[30][30] array) and the last pointer (one after all the argumenrs we want to pass) must be set to NULL. (I wonder how you expected to find a NULL in a char[30][30]).
The execvp() expects as second argument a char *const argv[]. This means an array of pointers to char. This is different from a char[30][30] which is represented in memory as 30x30 contiguous chars (so no pointer).
To solve this, define your structure
struct userinput {
char *anyargs[30]; //space for 30 char* pointers
};
You could as well define anyargs as char** and initalize if dynamically with (char**)calloc(number_of_args+1,sizeof(char*))
Later, assign directly the pointers:
inPtr->anyargs[0] = "ls"; //Hard code (or use strdup() )
inPtr->anyargs[1] = "-lh";
inPtr->anyargs[2] = NULL; // end of the argument list !!!
char **arrPointer; //Pointer to an array of char *
arrPointer = inPtr->anyargs;
Edit: Caution: "The array of pointers must be terminated by a NULL 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.