I want to outsource a string operation to a function and then ask the results in main. This doesn't work, and I don't understand why.
#include <stdio.h>
#include <string.h>
void splitRequest(char request[], char method[], char ressource[], char proto[]) {
method = strtok(request, " ");
ressource = strtok(NULL, " ");
proto = strtok(NULL, " ");
printf("\nResult:\n\nmethod:\t\t%s\nressource:\t%s\nproto:\t\t%s\n",method,ressource,proto);
}
int main()
{
char method[50], ressource[50], proto[50], request[50];
memset(method, '\0', 50);
memset(ressource, '\0', 50);
memset(proto, '\0', 50);
memset(request, '\0', 50);
strcpy(request,"Get /index.htm HTTP/1.1");
//rehash query
splitRequest(request, method, ressource, proto);
//check Results
printf("\nResult:\n\nmethod:\t\t%s\nressource:\t%s\nproto:\t\t%s\n",method,ressource,proto);
return 0;
}
In the splitRequest function all the arguments are pointers. And more importantly they are local variables. So all changes to them (like making them point anywhere else) will only affect the local variable, nothing else.
The solution is to copy to the memory instead. Perhaps something like
char *temp;
if ((temp = strtok(request, " ")) != NULL)
{
strcpy(method, temp);
}
// And so on...
A little elaboration about what I mean...
In C all function arguments are passed by value. That means their value is copied and the function only have a local copy of the value. Chaning the copy will of course not change the original value.
It is the same with pointers. When you call your splitRequest function the pointers you pass are copied. Inside the function the variable method (for example) is pointing to the memory of the array you defined in the main function. When you assign to this variable, like you do with
method = strtok(...);
you only modify the local variable, the local copy of the pointer. When the function returns the local variable method goes out of scope, and all changes to it are lost.
There are two solutions to this problem. One is to emulate pass by reference (something which C doesn't have, which is why it must be emulated), but that will not work as long as you have an array. Therefore the second and easiest solution: To copy to the memory pointed to by the local variable method, which is what I show above.
An important note though: When you call the splitRequest function, passing the arrays, the arrays themselves are not passed. Instead the arrays decays to a pointer to their first element, and inside the function the variables defined by the arguments are pointers and not arrays.
The call
splitRequest(request, method, ressource, proto);
is equal to
splitRequest(&request[0], &method[0], &ressource[0], &proto[0]);
And the function declaration
void splitRequest(char request[], char method[], char ressource[], char proto[])
is equal to
void splitRequest(char *request, char *method, char *ressource, char *proto)
So no the strings are not copied, only the pointers. Otherwise you would have gotten an error when assigning to the pointer, since you can't assign to arrays only copy to them.
Found another workaround even more effectiv:
#include <stdio.h>
#include <string.h>
void splitRequest(char request[], char **method, char **ressource, char **proto) {
*method = strtok(request, " ");
*ressource = strtok(NULL, " ");
*proto = strtok(NULL, " ");
printf("\nResult:\n\nmethod:\t\t%s\nressource:\t%s\nproto:\t\t%s\n",*method,*ressource,*proto);
}
int main()
{
char *method, *ressource, *proto, request[50];
memset(request, '\0', 50);
strcpy(request,"Get /index.htm HTTP/1.1");
//rehash query
splitRequest(&request, &method, &ressource, &proto);
//check Results
printf("\nResult:\n\nmethod:\t\t%s\nressource:\t%s\nproto:\t\t%s\n",method,ressource,proto);
return 0;
}
Related
I am new to C. Was writing this so it takes the strings from the passed array and makes it a single sentence. But I got this error, I am not good with arrays in C. I can use some help from you guys. I did search an answer for this and couldn't find.
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char smash(char arr[20][20]) {
char tmp[sizeof(arr)/sizeof(arr[0])];
for (int i=0; i < sizeof(arr)/sizeof(arr[0]); i++) {
strcat(tmp, arr[i]);
strcat(tmp, " ");
}
return tmp;
}
int main(){
char list[][6] = {"hello", "world"};
printf("%s", smash(list[]));
}
Error
error: expected expression before ']' token
printf("%s", smash(list[]));
^
There are quite a number of errors in this small piece of code.
First, to address the compiler error: list[] is not a valid expression. If you want to pass list to the function, leave the braces out:
printf("%s", smash(list));
This will then bring up another error. The function is expecting a char [20][20] as it's argument, but that's not what you're passing in. Since arrays as parameters are converted to a pointer, the argument type is actually char (*)[20] i.e. a pointer to an array of char of size 20. Note also that this conversion only occurs for the outermost array dimension, not all.
Since you're passing in a char [2][6] which gets converted to a char (*)[6] this is a type mismatch. So change the parameter to char arr[][6].
Then you're attempting to get the size of the array parameter inside of the function:
sizeof(arr)/sizeof(arr[0])
Since arrays cannot be directly passed to a function due to the conversion mentioned earlier, arr is actually a pointer and not an array, so you won't get the result you expect from this. You'll need to pass the number of array elements as a separate parameter.
Then you're calling strcat on tmp. This function will only work if the destination already has a null terminated string in it. Since tmp was not initialized or written to prior to the first call to strcat, you end up reading uninitialized bytes and potentially past the end of the array which will trigger undefined behavior.
This can be fixed by setting the first byte of the array to 0 before the loop to make it an empty string:
tmp[0] = 0;
for ...
Then there's the problem with the return type. The function is declared to return a char but you're giving a char * to the return statement, and at the point the function is called it is passed to printf where the %s format specifier is expecting a char * parameter.
So change the return type of the function from char to char *.
Finally, you're returning a pointer to a local variable in the function. This variable's lifetime ends when the function returns, so the returned pointer is invalid and using it will also trigger undefined behavior.
You'll need change tmp to a pointer and dynamically allocate memory for it using malloc. This also means you'll need to save the return value of the function in a separate variable which you can then pass to printf to print and then pass to free to free the memory.
After making all this changes, the resulting code should look like this:
char *smash(char arr[][6], int len) {
// enough for len strings plus len spaces
char *tmp = malloc(sizeof(arr[0]) * len + len + 1);
tmp[0] = 0;
for (int i=0; i < len; i++) {
strcat(tmp, arr[i]);
strcat(tmp, " ");
}
return tmp;
}
int main(){
char list[][6] = {"hello", "world"};
char *result = smash(list, sizeof(list)/sizeof(list[0]));
printf("%s", result);
free(result);
return 0;
}
I'm a C noob and I'm having problems with the following code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void split_string(char *conf, char *host_ip[]){
long unsigned int conf_len = sizeof(conf);
char line[50];
strcpy(line, conf);
int i = 0;
char* token;
char* rest = line;
while ((token = strtok_r(rest, "_", &rest))){
host_ip[i] = token;
printf("-----------\n");
printf("token: %s\n", token);
i=i+1;
}
}
int main(){
char *my_conf[1];
my_conf[0] = "conf01_192.168.10.1";
char *host_ip[2];
split_string(my_conf[0], host_ip);
printf("%s\n",host_ip[0]);
printf("%s\n",host_ip[1]);
}
I want to modify the host_ip array inside the split_string function and then print the 2 resulting strings in the main.
However, the 2 last printf() are only printing unknown/random characters (maybe an address?). Any help?
There are 2 problems:
First, you're returning pointers to local variables. You can avoid this by strduping the strings and freeing in the caller.
Second:
On the first call to strtok_r(), str should point to the string to be parsed, and the value of saveptr is ignored. In subsequent calls, str should be NULL, and saveptr should be unchanged since the previous call.
I.e. you must NULL for the first argument after the first iteration in the loop. Nowhere is it said that it is OK to use the same pointer for both arguments.
This is because the strtok_r is an almost drop-in replacement to the braindead strtok, with just one extra argument, so that you could even wrap it with a macro...
Thus we get
char *start = rest;
while ((token = strtok_r(start, "_", &rest))){
host_ip[i] = strdup(token);
printf("-----------\n");
printf("token: %s\n", token);
i++;
start = NULL;
}
and in the caller:
free(host_ip[0]);
free(host_ip[1]);
You are storing address of local variable (line) which is in stack.Stack is LIFO and has valid data for local variables in its stack memory during its function life time.after that, the same stack memory will be allocated to another function's local variables. So, data stores in line【50】's memory will be invalid after coming out of string_split function
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'm writing an nginx module in C and am having some super bizarre results. I've extracted a function from my module to test its output as well as the relevant nginx type/macro definitions.
I'm building a struct in my build_key_hash_pair function, then doing a printf() on the contents in main. When I printf the data inside the inner function, main's output is valid. When I remove the printf inside the inner function, main prints an empty string. This is confusing because after the function call to build_key_hash_pair I am not operating on the data except to display it. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct ngx_str_t {
size_t len;
char *data;
} ngx_str_t;
typedef uintptr_t ngx_uint_t;
typedef struct key_hash_pair {
ngx_uint_t hash;
ngx_str_t key;
} key_hash_pair;
#define ngx_string(str) { sizeof(str) - 1, (char *) str }
#define ngx_str_set(str, text) \
(str)->len = sizeof(text) - 1; (str)->data = (char *) text
#define ngx_hash(key, c) ((ngx_uint_t) key * 31 + c)
#define ngx_str_null(str) (str)->len = 0; (str)->data = NULL
void build_key_hash_pair(key_hash_pair *h, ngx_str_t api_key, ngx_str_t ip);
int main (int argc, char const *argv[])
{
ngx_str_t api_key = ngx_string("86f7e437faa5a7fce15d1ddcb9eaeaea377667b8");
ngx_str_t ip = ngx_string("123.123.123.123");
key_hash_pair *pair;
pair = malloc(sizeof(key_hash_pair));
build_key_hash_pair(pair, api_key, ip);
printf("api_key = %s\n", api_key.data);
printf("ip = %s\n", ip.data);
printf("pair->key = %s\n", pair->key.data);
printf("pair->hash = %u\n", (unsigned int)pair->hash);
return 0;
}
void build_key_hash_pair(key_hash_pair *h, ngx_str_t api_key, ngx_str_t ip)
{
ngx_str_null(&h->key);
char str[56];
memset(str, 0, sizeof(str));
strcat(str, api_key.data);
strcat(str, ip.data);
ngx_str_set(&h->key, str);
ngx_uint_t i;
for (i = 0; i < 56; i++) {
h->hash = ngx_hash(&h->hash, h->key.data[i]);
}
}
Here is the output when I do a printf("hello") inside the build_key_hash_pair function:
helloapi_key = 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
ip = 123.123.123.123
pair->key = 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8123.123.123.123
pair->hash = 32509824
And here is the (bizarre) output when I do NOT printf inside build_key_hash_pair:
api_key = 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
ip = 123.123.123.123
pair->key =
pair->hash = 32509824
As you can see, pair->key has no data. In gdb, if I breakpoint right after the call in main to build_key_hash_pair, pair->key contains the appropriate data. But after the first call to printf, it is blanked out. The memory address stays the same, but the data is just gone. Can anyone tell me what in the world I'm doing wrong?
This line is a problem:
ngx_str_set(&h->key, str);
Here str is a local variable, and you are putting a pointer to it inside h->key, which will be returned to the caller. After build_key_hash_pair returns, the pointer will no longer be valid. When you didn't call any other function, the pointer happened to still point to the same value, but this is not something you can rely on. The call to printf overwrote that part of the stack.
What you need is either to dynamically allocate the string with malloc or strdup, or put an array inside the key_hash_pair struct to hold the key (possible if the key is always the same size).
build_key_hash_pair uses stack-based array str to populate the data field in the key of h. When you exit from the function, that pointer is no longer valid since str goes out of scope.
Your results could be anything from apparently correct operation to a program failure. printf in the function will work, but definitely not if called afterwards. ngx_str_set needs to allocate memory and copy the text string into it (to be freed later of course).
I would replace all those macros with functions or inline code, personally.
The problem is in the build_key_hash_pair function, specifically with the stack variable char str[56]; which is assigned to the key_hash_pair via the macro ngx_str_set.
Since the stack frame containing char str[56]; disappears when the function returns, all bets are off for the value of of the pair's data once the function ends.
If i have char* str; how do I write a function that accepts str and can make changes to str so that, the changes persist after the function returns?
what I have is:
char *str = (char *) malloc(10);
sprintf(str, "%s", "123456789");
//str points to 1
move_ptr(&str);
//str points to 2
void move_ptr(char** str)
{
*str++;
}
is there a better way to do that?
Just access the data through the pointer, in the function:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
void change_string(char *str)
{
size_t i;
/* As an example, make it all upper case. */
for(i = 0; str[i]; ++i)
str[i] = toupper(str[i]);
}
int main(void)
{
char buffer[32];
char *str = buffer;
strcpy(str, "test string");
change_string(str);
printf("it's now %s\n", str);
return EXIT_SUCCESS;
}
Come to think of it, you'll notice that the standard strcpy() function is exactly of the category you describe. It's a very common operation in C.
UPDATED: The question has been significantly rewritten, now it seems to be more about changing the pointer itself, rather than the data. Perhaps this was the meaning all along, but I didn't understand.
The solution in the question is fine, but personally I find it more convenient to work with return values, if possible:
char * change_pointer(char *str)
{
return str + 1;
}
int main(void)
{
char *str = "test string";
printf("now '%s'\n", str);
str = change_pointer(str);
printf("now '%s'\n", str);
return EXIT_SUCCESS;
}
The pointer(s) could of course also be const-declared, and should be if no changes to the buffered text are needed.
Question changed
If your pointer points to readonly data, you can't change what it points to.
When one writes
char *data = "forty two";
that "forty two" is readonly data; and you can't change what the pointer data points to whether directly or through a function call.
To get a 'string' initialized from a literal constant, instead of assigning a pointer to the literal constant, copy the characters to an array
char data[] = "forty two";
Now data is an array of 10 characters (9 for the letters and space + 1 for the NUL terminator) which you can change at will.
Your example may be over simplified, but just in case... Be careful of doing things like this because you're going to leak memory. After your function call, you no longer have a pointer to (part of) the original memory you allocated.
As mentioned by unwind, returning the new pointer may be a better choice. While it achieves the same goal, it makes it more obvious that you need to keep the original pointer around for the purposes of releasing the memory. The counter argument being that it gives the impression that you can free the original pointer once you have the return value, which you can't do because they both point at (different locations) in the same memory block.