简体   繁体   中英

Allocating a buffer/string

Let's say I have a function as follows:

void function(const char * str) {
    // initialize string
}

Is there considered a "standard" way or best practice to initialize a new string for a function? For example, what would be the difference in using the following approaches:

char * str1;

char * str2 = malloc(len);
char str3[len];

Is there a time when I should use one over the other? It seems like using char * str2 = malloc(len); would be the most flexible of the above.

char* str1;

This line is declaring a new character array. You can't use this as a string yet, since you need to set its value to an address of a block of memory using malloc, calloc, or realloc. As mentioned, this needs to be freed when you're done using it to avoid memory leaks.

char str1[];

This line will not compile, since you are trying to declare a static array with no size.

char * str3 = malloc(len);

This line is the same as above, except you are initializing your pointer to a block of memory. This can now be used as a string with a length of len-1 to make space for the null terminator. Again, this needs to be freed at the end of use to avoid memory leaks.

char str4[len];

This line is similar to the line above, except this is a static array that can not change size. 'len' needs to be a const integer known during compile time. This type of array does not need to be freed and is lost when you leave the scope of where it is declared. This can store a string of len-1 to make space for the null terminator.

As mentioned above, this type can be initialized like this: char str4[] = {'n', 'e', 'w', ' ', 's', 't', 'r', 'i', 'n', 'g', '\\0'};

char* str3 = "new string";

This line is NOT the same as the above line. This line will create a string CONSTANT which can not be changed during runtime. Do note however that a null terminator is automatically added to the end of string constants.

We like the following:

void function(char ** str) {
    // initialize string
    *str = malloc(some length calculation);
    if (!str) return;
    strcpy(str, something); // Or maybe it isn't strcpy.
}

int main()
{
    // ...
    char * some_string = NULL;
    // ...
    function(&some_string);
    ///...
    if (some_string) {
        // do something with some_string
    }

    // ...
    free(str);
}

Another well-liked pattern:

char *function() {
    // initialize string
    char *str = malloc(some length calculation);
    if (!str) return str;
    strcpy(str, something); // Or maybe it isn't strcpy
    return str;
}

int main()
{
    // ...
    char *some_string = function();
    ///...
    if (some_string) {
        // do something with some_string
    }

    // ...
    free(str);
}

If it really is const sometimes we can use this simpler method:

const char *function()
{
    if (condition)
         return "some string";
    // ...
    if (some other condition)
         return "some other string";
    // ...
    return "something completely different";
}

int main()
{
   //...
   const char *str = function();
   //...
}

The comments are all valid points.

As long as you properly handle freeing the memory and also make sure any memory allocated with malloc is in fact allocated, there's a couple differences I can think of.
One is that using malloc() allows you to be more dynamic and allocate strings of different lengths, whereas with char str[10] (or whatever length you need) means you have to know in advance the maximum length of the expected string. If you're accepting user input, you must make sure you do not accept more than the string will hold or else you open up a security hole.

Another possible issue, which may or may not ever be applicable to your situation, is that any string created with char str[10] will be allocated on the stack if it's done within a function. On 8-bit embedded systems, you'll have to make sure that the string doesn't cross a 256 byte memory boundary, or else the stack pointer can wrap around to the beginning of that memory segment and can lead to some difficult to diagnose issues. You'd have to look at the assembly to see if the stack pointer is only 8-bit wide (this bit me one time and was a strange issue to debug. It wasn't a string issue, but a large struct issue, but strings would do it too). Whereas malloc() will be on the heap, that is if your embedded processor has a heap set up (not common on 8-bit but possible). Note: the string doesn't have to be 256 bytes long, but you could have more local variables before it, or the function could be far down the call stack, pushing a shorter string across a boundary.

Manual memory manage is always a fraught issue, so my preference is to use stack allocation as much as possible, but it really depends on your application.

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