Live data from Hacker News

Understanding Memory Management, Part 1: C

educatedguesswork.org

61–70 of 95 posts

Re: Understanding Memory Management, Part 1: C

#61

Earlier quoted context omitted.

>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.

It’s an option on most systems.

Maybe not in embedded work - but in that case you might want to preallocate memory anyway.

Re: Understanding Memory Management, Part 1: C

#62
The example strdup implementation:

  char *strdup(const char *str) { 
    size_t len = strlen(str);
    char *retval = malloc(len);
    if (!retval) {
      return NULL; 
    }
    strcpy(retval, str);
    return retval;
  }
Has a very common defect. The malloc call does not reserve enough space for the NUL byte required for successful use of strcpy, thus introducing heap corruption.

Also, assuming a NULL pointer is bitwise equal to 0 is not portable.

Re: Understanding Memory Management, Part 1: C

#63

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 was looking for a place to hang this comment and here's as good as any: the right way to handle this problem in most C code is to rig malloc, realloc, and strdup up to explode when they'd return NULL. Proper error handling of a true out-of-memory condition is pretty treacherous, so most of the manual error handling stuff you see on things like realloc and malloc are really just performative. In an application setting like this --- not, like, the world's most popular TLS library or something --- aborting automatically on an allocation failure is totally reasonable.

Since that's essentially what EKR is doing here (albeit manually), I don't think this observation about losing the original `lines` pointer is all that meaningful.

Re: Understanding Memory Management, Part 1: C

#64
post #60

Earlier quoted context omitted.

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 total…

I agree. The fact that Rust and Go will panic by default in this situation is pretty close to dispositive on what the right thing to do in (most) C code is.

Re: Understanding Memory Management, Part 1: C

#65

The example strdup implementation: char *strdup(const char *str) { size_t len = strlen(str); char *retval = malloc(len); if (!retval) { return NULL; } strcpy(retval, str); return retval; } Has a very common defect. The malloc call does not reserve enough space for the NUL byte required for successful use of strcpy, thus introducing heap corruption. Also, assuming a NULL pointer is bitwise equal to 0 is not portable.

re: the bitwise representation of NULL, evaluating a pointer in a Boolean context has the intended behavior regardless of the internal representation of a null pointer.

See the C FAQ questions 5-3 and 5-10, et al. https://c-faq.com/null/

Re: Understanding Memory Management, Part 1: C

#66
post #3

Great post for intermediary programmers, who started programming in Python, and who should now learn what's under the hood to get to the next level of their education. Sometimes (perhaps most of the time), we should ignore the nitty gritty details, but the moment comes where you need to know the "how": either because you need more performance, sort out an issue, or do something that requires low-level action. There a…

Which is why it sucks the top comments are pedantry over what is proper C code, or other comments are about how to optimize the article's code, all missing the point that we're learning concepts that can be corrected later

Re: Understanding Memory Management, Part 1: C

#67
post #41

Just no. address = X length = *X address = address + 1 while length > 0 { address = address + 1 print *address }

Author here. You're quite right that this isn't the thing you would normally do. I'm just trying to help people work through the logic of the system with as few dependencies as possible, hence this (admittedly yucky) piece of pseudocode which isn't really C or Rust or Python or anything...

At least update "length" for the for loop since it would go into an infinite loop the way it is now in any of those languages.

Re: Understanding Memory Management, Part 1: C

#68
post #39

Earlier quoted context omitted.

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 coul…

Now split the struct across registers in C.

You are aware that some of those casting tricks are UB, right?

Re: Understanding Memory Management, Part 1: C

#69

Earlier quoted context omitted.

I'll narrow my scope more explicitly: close(x) is not memory management - not at the user level. This should be done. free(p) has no O/S side effects like this in C - this can be not-done if you don't malloc all your memory. You can get away with not de-allocating program memory, but (as mentioned), that has nothing to do with freeing Os/ kernel / networking resources in C.

Most kernel resources are fairly well behaved, as they will automatically decrement their refcount when a process exits. Even mutexes have a "robust" flag for this exact reason. Programs which rely on destructors or any other form or orderly exit are always brittle and should be rewritten to use atomic operations.

Which kernel, on which specific OS?

This is a very non portable assumption, even we constrain it to only across UNIX/POSIX flavours.

Re: Understanding Memory Management, Part 1: C

#70
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.

Most code correctness is pointless until it isn't, yes
Post reply on HN