Live data from Hacker News

Understanding Memory Management, Part 1: C

educatedguesswork.org

51–60 of 95 posts

Re: Understanding Memory Management, Part 1: C

#51
post #9

This isn't proper usage of realloc: lines = realloc(lines, (num_lines + 1) * sizeof(char *)); In case it cannot service the reallocation and returns NULL, it will overwrite "lines" with NULL, but the memory that "lines" referred to is still there and needs to be either freed or used. The proper way to call it would be: tmp = realloc(lines, (num_lines + 1) * sizeof(char *)); if (tmp == NULL) { free(lines); lines = NUL…

Very odd that an article trying to teach memory management would miss this, this should be common knowledge to anyone who used realloc, just like checking the return of any allocation call.

>checking the return of any allocation call

I would say this is pointless on many modern systems unless you also disable overcommit, since otherwise any memory access can result in a crash, which is impossible to check for explicitly.

Re: Understanding Memory Management, Part 1: C

#52
post #47

Earlier quoted context omitted.

What modern OS doesn't have the equivalent of mmap? Just som #ifdefs. I didn't know I'd ever hear "use malloc, because it's portable". sylware is pretty much right anyway. Try to avoid malloc, or write smaller allocators on top of malloc.

Why bother with some #ifdefs when the ISO C standard library already does the job?

Because you're probably writing a much larger program so some ifdefs aren't a big deal :-).

Re: Understanding Memory Management, Part 1: C

#53
> If we just concatenate the values in memory, how do we know where one line ends and the next begins? For instance, maybe the first two names are "jim" and "bob" or maybe it's one person named "jimbob", or even two people named "jimbo" and "b".

Don't we have a newline character? I thought we can read newline as `0xA` in Rust?

Re: Understanding Memory Management, Part 1: C

#54
This post caused me to create an account. This C code is not good. Writing C is absolutely harder than Python, but you're making it so much harder than it has to be. Your program is buggy as heck, has very finicky cleanup code, and so on.

Here's a much easier way to write the program:

1. Dump whole file into buffer as one string

2. Find newlines in buffer, replace with NULs. This also let's you find each line and save them in another buffer

3. Sort the buffer of all the lines you found

4. qsort the buffer

5. Print everything

6. Free both buffers

Or, as a C program: https://godbolt.org/z/38nq1MorM

Re: Understanding Memory Management, Part 1: C

#55
post #39

Earlier quoted context omitted.

In C, not all objects need to be their own allocated entity (like they are in other languages). They can be stored in-line within another object, which means the lifetime of that object is necessarily constrained by that of its parent. You could make every object its own allocated entity, but then you're losing most of the benefits of using C, which is the ability to control memory layout of objects.

As any systems programming language include those that predate C by a decade, and still it doesn't allow full control without compiler extensions, if you really want full control of memory layout of objects, Assembly is the only way.

In practice C let's you control memory layout just fine. You might need to use __attribute__((packed)), which is technically non standard.

I've written hardware device drivers in pure C where you need need to peek and poke at specific bits on the memory bus. I defined a struct that matched the exact memory layout that the hardware specifies. Then cast an integer to a pointer to that struct type. At which point I could interact with the hardware by directly reading/writing fields if the struct (most of which were not even byte aligned).

It is not quite that simple, as you also have to deal with bypassing the cache, memory barriers, possibly virtual memory, finding the erreta that clarifies the originaly published register address was completely wrong. But I don't think any of that is what people mean when they say "memory layout".

Re: Understanding Memory Management, Part 1: C

#56
post #9

Earlier quoted context omitted.

Very odd that an article trying to teach memory management would miss this, this should be common knowledge to anyone who used realloc, just like checking the return of any allocation call.

>checking the return of any allocation call I would say this is pointless on many modern systems unless you also disable overcommit, since otherwise any memory access can result in a crash, which is impossible to check for explicitly.

abort() isn't an option on all modern systems.

Re: Understanding Memory Management, Part 1: C

#57
post #25
post #12

Avoid as much as you can the C standard lib allocator, go directly to mmap system call with your own allocator if you know you won't use CPU without a MMU. If you write a library, let the user code install its own allocator.

"malloc" is a weakly-bound symbol that can be overridden, on every system I've used. I don't know if some standard defines it to be weak. Anyway the point is that malloc is not necessarily a call to the C standard library function. It can be anything.

The linker doesn't try to resolve symbols it's already seen while static linking. This doesn't require a weak linkage flag for overriding system library functions since libc is linked at the end by default when static linking or at runtime when dynamic.

Re: Understanding Memory Management, Part 1: C

#58

This isn't proper usage of realloc: lines = realloc(lines, (num_lines + 1) * sizeof(char *)); In case it cannot service the reallocation and returns NULL, it will overwrite "lines" with NULL, but the memory that "lines" referred to is still there and needs to be either freed or used. The proper way to call it would be: tmp = realloc(lines, (num_lines + 1) * sizeof(char *)); if (tmp == NULL) { free(lines); lines = NUL…

I feel like this comment is misleading because it gives the impression that the code in the article is wrong or unsafe, whereas I think it's actually fine? In the article, in the case when `tmp == NULL` (in your notation) the author aborts the program. This means there's no memory leak or unsafety. I agree that one can do better of course.

You're confusing the code with the program it compiles to. The program is fine, okay. But the code is only "fine" or "safe" if you view it as the final snapshot of whatever it's going to be. If you understand that the code also influences how it's going to evolve in the future (and which code doesn't?) then no, it's not fine or safe. It's brittle and making future changes more dangerous.

Really, there's no excuse whatsoever for not having a separate function that takes the pointer by reference & performs the reallocation and potential termination inside itself, and using that instead of calling realloc directly.

Re: Understanding Memory Management, Part 1: C

#59
post #47

Earlier quoted context omitted.

Why bother with some #ifdefs when the ISO C standard library already does the job?

Because you're probably writing a much larger program so some ifdefs aren't a big deal :-).

This is a silly argument because at the end of the day, once you make your code portable, you've now duplicated 99% of malloc and free, and you've left a mess for the team or next guy to maintain on top of everything else. You've successfully lowered the abstraction floor which is already pretty low in C.

Re: Understanding Memory Management, Part 1: C

#60
post #9

Earlier quoted context omitted.

Very odd that an article trying to teach memory management would miss this, this should be common knowledge to anyone who used realloc, just like checking the return of any allocation call.

They treat an OOM situation as exceptional and immediately call abort() in case any allocation function returns NULL. The specification of these functions allows you to handle OOM situations gracefully.

> The specification of these functions allows you to handle OOM situations gracefully.

In theory, sure. But vanishingly little software actually deals with OOM gracefully. What do you do? Almost any interaction with the user may result in more memory allocations in turn - which presumably may also fail. It’s hard to even test OOM on modern systems because of OS disk page caching.

Honestly, panicking on OOM is a totally reasonable default for most modern application software. In languages like rust, this behaviour is baked in.

Post reply on HN