tezc / sc

Common libraries and data structures for C.
BSD 3-Clause "New" or "Revised" License
2.23k stars 239 forks source link

question: why pass by reference in `sc_str_substring((char** str, uint32_t start, uint32_t end)`? #124

Closed midnqp closed 1 year ago

midnqp commented 1 year ago

I'd say I am still new to learning to code in C. This library has been really helpful for me to see how C code is written in the real world. So, thank you!

I was wondering why pass the str by reference in some cases sc_str_destroy() and sc_str_trim(), because I think it's possible to work with pointers without it. For example:

void alter(char* str) {
  str[0] = 'f';
  free(str);
}

int main() {
  char* str = malloc(6);
  strcpy(str, "hello");
  alter(str);
}
tezc commented 1 year ago

Hi @midnqp, glad this repo is being helpful to you.

Just to be clear, you wonder why it passes double pointer e.g. char** instead of char* right? Reason is that some functions might need to change the value of the pointer.

For example, here: see the link This function makes another allocation and updates the value of the pointer. To do that, you need to get char**.

So, you need to get "pointer to pointer" if you need allocate new memory in your function. In other cases, as in your example above, if you just want to change its content (its characters) it is not necessary.

One exceptional case is sc_str_destroy(). You may use char* as it just calls free() for the pointer. But just to make it safe to call twice, this function sets pointer to NULL. See: link

So, user does not have to remember if it called sc_str_destroy() before, it will be safe to call it multiple times.

e.g:

char *c = sc_str_create("hello");
sc_str_destroy(&c); // This call will set c = NULL 
...
sc_str_destroy(&c); // Safe to call it twice.

// Otherwise you would need to write 
if (c != NULL)  {
    sc_str_destroy(c);
    c = NULL;
}
// So, it saves you adding these NULL if checks in your application.

Let me know if this is clear? Cheers!

midnqp commented 1 year ago

Wow, this is really simple and clear as a crystal ✨ Thanks for your time in explaining this to me! I actually did notice you mention that everytime we use set, create, append a new pointer is created. Perfect!

However, we could extend the previous pointer's memory. But.. oh, realloc() still malloc()s under the hood. So we don't have a choice other than to create a pointer everytime.

Wow, this library is well-built.

tezc commented 1 year ago

Be careful with realloc, it is not guaranteed to return the same value even when you call it to shrink the memory. I've seen that is happening, I had to debug a painful issue :)

So, you don't have any other way, you have to get char** if you want to (potentially) update the pointer.

midnqp commented 1 year ago

One last thing: kindly elaborate a tiny bit on this please? "it is not guaranteed to return the same value" - I understand realloc() returns a new pointer, but not the same value?

tezc commented 1 year ago

I understand realloc() returns a new pointer, but not the same value?

My point is realloc() may return same value on some calls and it may return another value on other calls. Because allocator (malloc(), realloc() implementation) is free to return whatever it wants.

void *x = malloc(10);

// y points to same memory with x
void *y = x;

// Extend memory allocation. After realloc(), will y be equal to x? 
y =  realloc(y, 20);

// Assume that x points to a memory which has already 20 bytes empty space, 
// then allocator can mark your allocation as 20 bytes and it can return the same pointer. (y == x);
//
// If there is not enough space (assume x points to 10 bytes empty space and there are used bytes 
// by someone else just after that) , allocator has to return another pointer to you (y != x)

So, my point is we cannot say realloc() will return same or different value. It totally depends on allocator implementation. Your interpretation is correct, if we assume realloc() will return new pointer all the time, we won't make mistakes but as you can see above, in reality, that might not be true :)

Let me know if it is clear.

tezc commented 1 year ago

@midnqp please feel free to open issue if you have other questions! Cheers!