This question already has answers here:
How do I modify a pointer that has been passed into a function in C?
(7 answers)
Closed 3 years ago.
I've made a function that allows the input of a string via keyboard. This function has two arguments: the maximum possible length of the string and a pointer to char. What happens inside the function is that an array of characters, which has as many elements as the maximum length, gets declared and then the string given by the user gets temporarly stored in that very array. Once the acquisition of the string is done, I use the calloc function to allocate just the right amount of memory to store that same string in the pointer to char that has been passed as an argument.
int main(void)
{
char* test;
stringInput(test, 10);
printf("%s", test);
return 0;
}
void stringInput(char* string, int maxStringLength)
{
char tempString[maxStringLength];
//STRING GETS PROPERLY STORED IN tempString
string = (char*)calloc(strlen(tempString)+ 1, sizeof(char));
strcpy(string, tempString);
return;
}
This sorts of work, meaning that if I try to print "string" before this function hits return, the program actually displays what it is supposed to. However, when I try to print "test" in the main function, it doesn't print anything, which means that stringInput isn't modifying the pointer that gets passed to it. I've further confirmed this by printing the address of "test" before the function call, after the calloc line and again after the function call, which showed me that it changes after the calloc but then gets back to its previous value when the function ends.
How can I solve this problem?
The problem here is, test itself is passed by value, which is stored in string, and any change you make to string will not reflect back to test.
You need to pass a pointer to test, if you want to modify test itself.
Something like
stringInput(&test, 10);
and
void stringInput(char** string, int maxStringLength)
{
char tempString[maxStringLength];
//STRING GETS PROPERLY STORED IN tempString
*string = calloc(strlen(tempString)+ 1, sizeof(char)); // no need to cast
if (!string) {
printf("error in calloc!!\n");
return;
}
strcpy(*string, tempString);
return;
}
When you calloc into string, you only modify the local copy of string, not the test variable. What you may want to do is pass in and operate on a pointer to your char pointer.
You could change you function signature to:
void stringInput(char **string_p, int maxStringLength)
Then, replace you usages of string with *string.
Finally, you would call your function passing in a pointer to test, not its value:
stringInput(&test, 10);
This is one way of doing things, though you could also return a pointer, depending on how you want to structure things.
Related
I'm in a basic C programming course and I'm trying to create a hangman game. I've been stuck with a problem for the last three hours and I'm not getting any wiser.
Basically, I've created a function which reads a random line from a text file and then copies it to a string. Afterwards, I want to copy that string to another string outside off the function. This is because the main game is supposed to be completely built with functions.
This is the function that reads a random word from the text file and copies it to a string:
char datorns_val()
{
char ordlista[20];
char valt_ord[20];
int raknare = 0;
srand(time(NULL));
random = rand()%10+0;
ptr_file =fopen("hangman.txt","r");
if (!ptr_file)
{
return 1;
}
while (fgets(ordlista,20, ptr_file)!=NULL)
{
raknare++;
if (raknare == random)
strcpy(valt_ord, ordlista);
}
return valt_ord;
}
After this is done, I want to copy the word located in valt_ord to another string, and that's when I'm unsure about what to do:
char word[20];
strcpy(word,datorns_val());
I'm getting two errors that says:
Invalid conversion from 'char' to 'const char*'
and
initializing argument 2 of 'char* strcpy(char*, const char*)'
Am I on the right track here with using strcpy() twice or am I completely lost? I tried my build without a function structure and simply typing out all the code on after another and it works, if a replace the second strcpy() with a simple char word = valt_ord.
Thanks, Jonathan
(Sorry if my code is hard to understand, I'm swedish and my second language is English)
Currently you're returning a character, which is not of much use, since you need a string that will outlive the function which creates it. You should return a dynamically allocated string (using a pointer) for this.
char* datorns_val()
{
// ... your current code
char *ret_str = malloc(20);
strcpy(ret_str, valt_ord);
return ret_str;
}
At the end where you use it, you should free it when done.
char *result = datorns_val();
// use result
free(result);
result = NULL;
Alternatively, if you're sure that the function which is calling the datorns_val is the only one which is going to use the result, then I'd recommend something else which doesn't involve dynamic memory alloc/decalloc (malloc/free). Pass the string to be loaded to datorns_val.
int datorns_val(char (*str_ptr)[20]) // pointer to an array of 20 chars
{
// use str_ptr after dereferencing it to get back the char array
// say you want to copy "abc" to it
strcpy(*str_ptr, "abc");
return 0; // to denote success, you may return -1 for failure
}
// caller's end
char result[20] = "";
int success = datorns_val(&result); // pass the array by reference
Read more about arrays and pointers to know more about them.
Your function datorns_val is declared to return char while in fact it returns valt_ord that is of type char[]. Also there is a way bigger problem valt_ord is a local variable so even if you change the declaration the code will not work. You will need to allocate valt_ord dynamically(using malloc) or to pass it as function argument.
argument 2 of strcpy needed string but your function returning char.
man strcpy.
you trying to return local variable, it will be destroyed when you move out of function because it stores the data in stack. Use malloc
see this SO question for further clarification
I am trying to write 3 function:
the first one: "read_comp" initialize a char pointer and assign it what function "readAndRemove" return.
readAndRemove read line from the user and remove any spaces before the string and return a pointer to the string without the spaces in the start.
then the "read_comp" print the string got by "readAndRemove" - the one without the spaces.
the last function - the one that i have problem with...
function "findComplex":
what i am trying this function to do is just to get char pointer and print the string that function got.
void read_comp(void)
{
char *str = readAndRemove();
printf("%s\n",str);
findComplex(&str);
}
-------------
char * readAndRemove() /**function to read rest of input and remove first space**/
{
char tempChar[30];
fgets(tempChar,30,stdin);
char* ptr = strtok(tempChar, " ");
return ptr;
}
--------------
void findComplex(char* str)
{
printf("in findComplex:%s\n",str);
}
(sorry if the start was irrelevant but i thought maybe there is problem with the way i am doing everything...)
so i tried to fix and change few things:
change this: define char *str; as global parameter
and chanege the function:
void read_comp(void)
{
*str = readAndRemove();
printf("%s\n",str);
findComplex(str);
}
char * readAndRemove() /**function to read rest of input and remove first space**/
{
char tempChar[30];
fgets(tempChar,30,stdin);
char* ptr = strtok(tempChar, " ");
return ptr;
}
void findComplex(char* str)
{
printf("%s\n",str);
printf("in findComplex:%s\n",str);
}
The variable str in the read_comp function is already a pointer. Your use of the address-of operator & makes that a pointer to a pointer (i.e. type char **). Just make sure the findComplex function is prototyped before you call it, and don't use the address-of operator.
You have a larger problem though, and that is that the readAndRemove function returns a pointer to a local variable. Remember that local variables are stored on the stack, and that when a function returns that stack space is reclaimed to be reused by other function calls. Create the array in the read_comp function instead, and pass it together with its size to the readAndRemove function.
If you enable warnings in your compiler (and I'm saying if as in "please do this!") you would get a warning saying "returning pointer to local variable" or something like that for this:
char * readAndRemove() /**function to read rest of input and remove first space**/
{
char tempChar[30];
fgets(tempChar,30,stdin);
char* ptr = strtok(tempChar, " ");
return ptr;
}
You MUST not return pointers to local variables, because the space used by tempchar (which ptr will point into) is going to be reused by the next function when you return from this function - and most likely the next function will write something OTHER than your string into this memory.
The solution, I would suggest, is to move tempchar up to read_comp() [1], and pass the string to readAndRemove.
[1] Please try to decide whether you use "camelcase" or "_" names. Either your functions should be read_and_remove and read_comp or readAndRemove and readComp. I almost wrote it wrong because I expected to find the same style in both functions - this sort of thing can drive you mad when you later try to change something.
I need a working code for a function that will return a random string with a random length.
What I want to do would be better described by the following code.
char *getRandomString()
{
char word[random-length];
// ...instructions that will fill word with random characters.
return word;
}
void main()
{
char *string = getRandomString();
printf("Random string is: %s\n", string);
}
For this, I am strictly forbidden to use any other include than stdio.h.
Edit: This project will be adapted to be compiled for a PIC Microcontroller, hence I cannot use malloc() or such stuff.
The reason why I use stdio.h here, is for me to be able to inspect the output using GCC.
Currently, this code gives this error.-
“warning: function returns address of local variable [enabled by default]”
Then, I thought this could work.-
char *getRandomString(char *string)
{
char word[random-length];
// ...instructions that will fill word with random characters.
string = word;
return string;
}
void main()
{
char *string = getRandomString(string);
printf("Random string is: %s\n", string);
}
But it only prints a bunch of nonsense characters.
There are three common ways to do this.
Have the caller pass in a pointer to (the first element of) an array into which the data is to be stored, along with a length parameter. If the string to be returned is bigger than the passed-in length, it's an error; you need to decide how to deal with it. (You could truncate the result, or you could return a null pointer. Either way, the caller has to be able to deal with it.)
Return a pointer to a newly allocated object, making it the caller's responsibility to call free when done. Probably return a null pointer if malloc() fails (this is always a possibility, and you should always check for it). Since malloc and free are declared in <stdlib.h> this doesn't meet your (artificial) requirements.
Return a pointer to (the first element of) a static array. This avoids the error of returning a pointer to a locally allocated object, but it has its own drawbacks. It means that later calls will clobber the original result, and it imposes a fixed maximum size.
None if these is an ideal solution.
It points to nonsense characters because you are returning local address. char word[random-length]; is defined local to char *getRandomString(char *string)
Dynamically allocate the string with malloc, populate string, and return the returned address by malloc. This returned address is allocated from the heap and will be allocated until you do not manually free it (or the program does not terminate).
char *getRandomString(void)
{
char *word;
word = malloc (sizeof (random_length));
// ...instructions that will fill word with random characters.
return word;
}
After you have done with the allocated string, remember to free the string.
Or another thing can be done, if you cannot use malloc which is define the local string in the getRandomString as static which makes the statically declared array's lifetime as long as the program runs.
char *getRandomString(void)
{
static char word[LENGTH];
// ...instructions that will fill word with random characters.
return word;
}
Or simply make the char word[128]; global.
As I understand, malloc is not an option.
Write a couple of functions to a) get a random integer (strings length), and b)a random char.
Then use those to build your random string.
For example:
//pseudocode
static char random_string[MAX_STRING_LEN];
char *getRandomString()
{
unsigned int r = random_number();
for (i=0;i<r;i++){
random_string[i] = random_char();
}
random_string[r-1] = '\0';
}
If you are not allowed to use malloc you'll have to declare an array that can be the maximum possible size at file scope and fill it with random characters.
#define MAX_RANDOM_STRING_LENGTH 1024
char RandomStringArray[MAX_RANDOM_STRING_LENGTH];
char *getRandomString(size_t length)
{
if( length > ( MAX_RANDOM_STRING_LENGTH - 1 ) ) {
return NULL; //or handle this condition some other way
} else {
// fill 'length' bytes in RandomStringArray with random characters.
RandomStringArray[length] = '\0';
return &RandomStringArray[0];
}
}
int main()
{
char *string = getRandomString(100);
printf("Random string is: %s\n", string);
return 0;
}
Both of your examples are returning pointers to local variables - that's generally a no-no. You won't be able to create memory for your caller to use without malloc(), which isn't defined in stdio.h, so I guess your only option is to make word static or global, unless you can declare it in main() and pass the pointer to your random string function to be filled in. How are you generating random numbers with only the functions in stdio.h?
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);
}
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;
}