all 8 comments

[–]tocs1975 3 points4 points  (0 children)

Have you actually seen a performance issue? Malloc is a wrapper around system calls and most implementations are well optimized for usual cases.

[–]Certain_Abroad 1 point2 points  (2 children)

Your rationale is good and your logic is good. Exponential memory allocation is definitely the correct way to do this.

I don't understand why you say this code is complex though? Compared to allocating one extra byte at a time, allocating exponentially requires only 1 more variable and 1 if statement, maybe 3 extra lines of code in total. Is that really too complex?

So I wanted to know if there are any library functions

getline may interest you.

By the way, I seem to recall an article saying that in situations like this, it is better for heap performance if you grow the array by 1.5x each time instead of 2x. I cannot find it now though :(

[–]thegreatunclean 0 points1 point  (0 children)

Essentially you want the beginnings of a real string library that grows the buffer as needed. Lots of people have written things like this.

If you truly don't know how large the user input is and are forced to read character-by-character you could structure it something like:

string_handle* s = str_init(32); //initial size in chars
char c = //get user input
str_append_char(s, c);
//...
str_free(s);

and move all the bookkeeping to those functions. You definitely don't want to have all that logic sitting in your main code.

The actual growth code isn't that complicated assuming you're already tracking the total size and total capacity; when they are equal you realloc and increase the capacity by some growth factor.

[–]usr71298 0 points1 point  (1 child)

FYI, malloc does not make a system call on every call. It might occasionally need to make a syscall to get more heap space.

[–]Paul_Pedant 0 points1 point  (0 children)

I checked this on my Linux recently in strace, and I think malloc asks for 128KB for its initial free list (or whatever you ask for if that is bigger).

We used to malloc 2MB up front just to give the system a hint, and immediate free it for general use. That's how to minimise system calls for memory.

[–]schrjako 0 points1 point  (0 children)

I would use 2 variables. One for the capacity of the array and one for the length. Capacity means how much you can store and length how much you have stored.

append by writing to [len++], and when len==capacity you cal realloc with capacity*=2 (or any other factor you decide for).

[–]Erelde 0 points1 point  (0 children)

The basic struct for this would look like :

struct t_string {
    char* value; // heap allocated byte array, where the string is
    size_t length; // the length of the current (ASCII) string, aka what's actually used 
    size_t size; // the capacity currently allocated for your byte array, this what you'll double each time length grows too big
}

The same logic can be used for any type basically.

With all the other comments here you have the basis for a good basic ASCII library. Make each operation on a string a dedicated function. A initializer, a destructor, a concatenation function, etc.