简体   繁体   中英

Returning populated string array from function C

It's the first time posting so I apologise for any confusion:

I am writing a function like this:

int myFunc(char* inputStr, int *argCTemp, char** argVTemp[]);

The purpose of my function is to take a copy of the input string (basically any user input) and then use strtok to convert it to tokens and populate an array via an array pointer ( argV ). When myFunc is finished, hopefully I have the argument count and array of strings from my inputStr string.

Here is an example of how I call it:

int main(int argc, char** argv[])
{
    int argCTemp = -1;
    char** argVTemp; 

    // 1 Do Stuff
    // 2 Get input string from user 
    // 3 then call myfunc like this:

    myFunc(inputStr, &argCTemp, &argVTemp);

    // 4: I get garbage whenever I try to use "argVTemp[i]" 
}

My Questions: How should I best do this in a safe and consistent way. How do the pro's do this?


  1. I don't use malloc because:

    • I don't know the number of arguments or the length of each for my input (to dynamically allocate the space). I figured that's why I use pointers
    • since I declare it in the main function, I thought the pointers to/memory used by argCTemp and argVTemp would be fine/remain in scope even if they are on the stack.
  2. I know when myFunc exits it invalidates any stack references it created, so that's why I sent it pointers from a calling function. Should I be using pointers and malloc and such or what?

  3. Last thing: before myfunc exits, I check to see the values of argCTemp and argVTemp and they have valid content. I am setting argCtemp and argVtemp like this:

     (*argCTemp) = argCount; (*argVTemp)[0] = "foo"; 

and it seems to be working just fine BEFORE the function exits. Since I'm setting pointers somewhere else in memory, I'm confused why the reference is failing. I tried using malloc INSIDE myFunc when setting the pointers and it is still becoming garbage when myFunc ends and is read by the calling function.

I'm sorry if any of this is confusing and thank you in advance for any help.

Since "don't know the number of arguments or the length of each for my input ", you can use malloc also. When your buffer abouting full, you should realloc your buffer. The better way: You needn't store whole input. A line, a token or a block is better. Just set a static array to store them. and maybe hash is better if your input more than 100 mb.

I'm sorry for my poor English.

You send an uninitialized pointer (you call is isn't correct as well, you don't need the & ) to the function, this pointer points to some random place and that is why you get garbage, you can also get segmentation fault.

You can do one of the two.

Per allocate a large enough array which can be static for example

static char * arr[MAX SIZE] and send it (char **)&arr in the function call, or run twice and use malloc.

You should also pass the max size, or use constant and make sure you don't pass it.


Lets say you the number of tokens in int n then

char * arr[] = malloc(sizeof(int *)*n);

this will create array of pointers, now you pass it to your populate function by calling

it with (char **)&arr , and use it like you did in your code

for example (*argVTemp)[0] = ; .

(when the array is not needed any more don't forget to free it by caliing free(arr))

Generally speaking, since you don't know how many tokens will be in the result you'll need to allocate the array dynamically using malloc() , realloc() and/or some equivalent. Alternatively you can have the caller pass in array along with the array's size and return an error indication if the array isn't large enough (I do this for simple command parsers on embedded systems where dynamic allocation isn't appropriate).

Here's an example that allocates the returned array in small increments:

static
char** myFunc_realloc( char** arr, size_t* elements)
{
    enum {
        allocation_chunk = 16
    };

    *elements += allocation_chunk;
    char** tmp = (char**) realloc( arr, (*elements) * sizeof(char*));

    if (!tmp) {
        abort();  // or whatever error handling
    }

    return tmp;
}

void myFunc_free( char** argv)
{
    free(argv);
}

int myFunc(char* inputStr, int *argCTemp, char** argVTemp[])
{
    size_t argv_elements = 0;
    size_t argv_used = 0;
    char** argv_arr = NULL;


    char* token = strtok( inputStr, " ");

    while (token) {
        if ((argv_used+1) >= argv_elements) {
            // we need to realloc - the +1 is because we want an extra 
            //  element for the NULL sentinel
            argv_arr = myFunc_realloc( argv_arr, &argv_elements);
        }

        argv_arr[argv_used] = token;
        ++argv_used;

        token = strtok( NULL, " ");
    }

    if ((argv_used+1) >= argv_elements) {
        argv_arr = myFunc_realloc( argv_arr, &argv_elements);
    }
    argv_arr[argv_used] = NULL;

    *argCTemp = argv_used;
    *argVTemp = argv_arr;

    return argv_used;
}

Some notes:

  • if an allocation fails, the program is terminated. You may need different error handling.
  • the passed in input string is 'corrupted'. This might not be an appropriate interface for your function (in general, I'd prefer that a function like this not destroy the input data).
  • the user of the function should call myFunc_free() to deallocate the returned array. Currently this is a simple wrapper for free() , but this gives you flexibility to do more sophisticated things (like allocating memory for the tokens so you don't have to destroy the input string).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM