Live data from Hacker News

Defer: Resource cleanup in C with GCCs magic

oshub.org

61–70 of 99 posts

Re: Defer: Resource cleanup in C with GCCs magic

#61
post #42
post #30

Earlier quoted context omitted.

> If I use defer I'd just need the keyword defer and the free() code. Yeah, and not accidentally forgetting to call it. That's the big part. And before "True Scotsman will always free/close/defer!" - No, no they won't. Unless the compiler screams at them, or its enforced via syntax constructs, it will always slip through the cracks.

Well I'd have to pay all the friction of writing up a new type, and in some cases the type gets cubersome. Doubly so if your codebase requires extra some friction like 1 header for each type. Also get over it. We got post-processor things like static analyzers, etc, and whatever AI code reminders/fixers that are coming up next. I'd prefer those over muddying up the code base.

> Also get over it. We got post-processor things like static analyzers, etc, and whatever AI code reminders/fixers that are coming up next.

Sure. But unless it's part of compiler, someone will not run it, or will run out of resources (no net or no tokens).

Defaults matter a ton.

Re: Defer: Resource cleanup in C with GCCs magic

#62
post #35

Earlier quoted context omitted.

Rust mandates that every field in a user-defined type is initialized at once. How do you propose to retrofit that into C without "constructors"?

C has had designated initializers since C99, if you want you can initialise every struct field at once.

I encourage you to read (at least) this section of this blog before making simplistic suggestions: https://thephd.dev/just-put-raii-in-c-bro-please-bro-just-on...

How do you mandate initialization, handle copies, move objects, prevent double frees? What's RAII without any of that?

Re: Defer: Resource cleanup in C with GCCs magic

#63

Earlier quoted context omitted.

Not having RAII is precisely the reason I prefer C over C++ or Rust. I WANT to be able to separate allocation from initialization. I'm currently working with Arduino code and the API is a mess. Everything has a second set of manual constructor/destructor, which bypasses type-safety entirely. All only to shoehorn having existing, but uninitialized objects into C++.

Both C++ and Rust allow that? Having niche behaviour not be the default makes sense, but both know it's needed and therefore allow it? (C++ lets you malloc and then placement new (just casting the pointer like C does is UB, but it's being fixed for trivial types) and Rust has both plain alloc and Box >) There are a lot of other reasons not to use them, but yours is a made up strawman.

> malloc

Yes, that's heap allocation. I'm talking about automatic allocation, by the compiler not getting a pointer from a library function. Like that:

    Connection connections[200];
This will call the constructor, which forces me to write the class in a way that has `bool initialized`, and provide a random other method with poses as a second constructor. And now every function has to do a check, whether the constructor was called on the object or I just declare it to be UB and completely loose type safety.

Re: Defer: Resource cleanup in C with GCCs magic

#64
post #50

Earlier quoted context omitted.

Not having RAII is precisely the reason I prefer C over C++ or Rust. I WANT to be able to separate allocation from initialization. I'm currently working with Arduino code and the API is a mess. Everything has a second set of manual constructor/destructor, which bypasses type-safety entirely. All only to shoehorn having existing, but uninitialized objects into C++.

> I WANT to be able to separate allocation from initialization. Which hardly ever makes sense, and is possible with clean C++ anyway...

> is possible with clean C++ anyway...

That's news to me; how?

Re: Defer: Resource cleanup in C with GCCs magic

#65
post #51

Earlier quoted context omitted.

Yes, it can. Why would you be checking the pointer for nullptr after you have dereferenced it? It makes no sense at all, so, compiler indeed can elide the nullptr check before dereferencing the ptr exactly because it is free to _always_ assume that the program is free of UB. To be more precise GCC says "eliminate useless checks for null pointers" and what I am saying that you can never be sure what in your code ended…

> Why would you be checking the pointer for nullptr after you have dereferenced it? It makes no sense at all Right. It's UB. And that's why the optimization in question is about removing that check. The only reason the optimization is valid for a C compiler to do, is that it can assume dereferencing a null pointer lands you in UB land. I'm sorry, either you are terrible at trying to explain things, or you have thorou…

I think where you are talking past each other is, that one is talking about temporal after and the other about causal after. The null check can be eliminated if the dereference happens temporally after, but causally before:

    if (ptr == NULL)
    {
        ...
    }

    ...

    int foo = ptr->some_field;

Re: Defer: Resource cleanup in C with GCCs magic

#66
post #32

Earlier quoted context omitted.

> on top of C. If we're referring to the "C is a subset of C++" / "C++ is a superset of C" idea, then this just hasn't been the case for some time now, and the two continue to diverge. It came up recently, so I'll link to a previous comment on it ( https://news.ycombinator.com/item?id=45268696 ). I did reply to that with a few of the other current/future ways C is proposing/going to diverge even further from C++, sin…

> this just hasn't been the case for some time now Which I find sad actually. The idea of C++ as a superset of C is really powerful, especially when mixing C and C++. A while ago I had a C project (firmware for a microcontroller) and wanted to bake the version and the compilation time into the firmware. I didn't find a way to do this in plain C, but in C++ you can initialize a global struct and it gets statically lin…

> I didn't find a way to do this in plain C

Not sure what you were running into. I routinely do this just fine.

> This didn't even use constexpr, just preprocessor trickery.

Isn't the preprocessor shared between C and C++?

> in C++ you can initialize a global struct and it gets statically linked into the output

That sounds to be doable just the same in C?

Re: Defer: Resource cleanup in C with GCCs magic

#67

Just use C++, it's its main feature on top of C.

> Just use C++, it's its main feature on top of C. If you want to and/or can, then go ahead. This is for those people who either don't want to, or can't, use C++. Are you suggesting only use C++ over C in all situations?

Yes.

Re: Defer: Resource cleanup in C with GCCs magic

#68
post #32

Just use C++, it's its main feature on top of C.

> on top of C. If we're referring to the "C is a subset of C++" / "C++ is a superset of C" idea, then this just hasn't been the case for some time now, and the two continue to diverge. It came up recently, so I'll link to a previous comment on it ( https://news.ycombinator.com/item?id=45268696 ). I did reply to that with a few of the other current/future ways C is proposing/going to diverge even further from C++, sin…

among the people that refuse C++ and stick to C, very few are willing to look at C23.

Re: Defer: Resource cleanup in C with GCCs magic

#69
post #51

Earlier quoted context omitted.

> Why would you be checking the pointer for nullptr after you have dereferenced it? It makes no sense at all Right. It's UB. And that's why the optimization in question is about removing that check. The only reason the optimization is valid for a C compiler to do, is that it can assume dereferencing a null pointer lands you in UB land. I'm sorry, either you are terrible at trying to explain things, or you have thorou…

I think where you are talking past each other is, that one is talking about temporal after and the other about causal after. The null check can be eliminated if the dereference happens temporally after, but causally before: if (ptr == NULL) { ... } ... int foo = ptr->some_field;

Ah yes, unless that conditional returns, UB indeed has time travelling properties. (I mean, spec wise. Manifesting as the compiler "reasoning" that "well I could legally put that load before the check, and that means it's not null, which means I don't need the check")

But this brings us back to the article: Why does the author say that there's no way to check for NULL in the free function? Maybe they are hinting at something completely unrelated to what we're saying here?

If that's where we failed to communicate, then that makes sense. Thanks, stranger!

Re: Defer: Resource cleanup in C with GCCs magic

#70
post #58
post #52

Earlier quoted context omitted.

1. No, dereferencing a null pointer will not "cause a sigsegv". It causes UB. In practice, in unix user space, yes it'll probably be SIGSEGV. 2. A null pointer is not a valid pointer: Yeah… Once again my question was "But when would it not be a valid pointer, and yet also not a null pointer? A null pointer we can check for easily." This code will NEVER deference a null pointer. Not under any compiler, not with any co…

A zero (0, not NULL!) pointer is a valid pointer in C/C++. It is not a UB, and it means one simple thing: «give me the contents of a memory cell (a byte, a word, a long word etc) at the address of 0». Old hardware designs used the address of 0 to store a jump address of the system boot-up sequence (i.e. firmware), and I personally wrote the code in C to inspect / use it in the unpriviledged hardware mode. The prevail…

> it means one simple thing: «give me the contents of a memory cell (a byte, a word, a long word etc) at the address of 0»

Well… sometimes. If you set a pointer to literal 0, you do not actually make that pointer point to address zero, from the C language's point of view. No, you are then setting it to be the null pointer. (c99 6.3.2.3 paragraph 3)

Now, what is the bit value of a null pointer? That's undefined.

So how do you even set a pointer to point to address zero? In the C standard, maybe if you set an intptr_t to 0 and then cast it to the pointer? Actually I don't know how null pointer interacts with intptr_t 0. Is intptr_t even guaranteed to contain the same bit pattern? I don't see it. All I see is that it's guaranteed to convert back and forth without loss. For all I can find in the spec, converting between intptr_t and pointer inverts the bits.

A null pointer "is guaranteed to compare unequal to a pointer to any object or function".

Did you put an object or function at address zero? Sounds pretty UB to me.

> modern systems […] SEGV

I already agreed with you on this. I mean… now modern systems don't let applications map address zero (actually, is that always true? I know OpenBSD stopped allowing it after some security holes. I'm too lazy to check if Linux did too)

More info at https://stackoverflow.com/questions/63790813/allocating-addr...

In any case, this is a fix that's only like 10 years old (or I'm old and it's actually 20). It used to be possible.

> Embdedded systems that do not have a MMU will allow *ptr where «ptr» is zero to proceed happily.

This is absolutely not true. An embedded system could have I/O mapped to address zero reboot the machine on read or write. And that'd be perfectly fine for the C language spec, since C doesn't allow dereferencing a null pointer.

MMU is not the only way memory becomes magic. In fact, it's probably the LEAST of the magic memory mapping that can happen.

> with nothing else to debate.

I mean… you're just wrong. I'm not conflating unrelated things. I'm correcting multiple unrelated mis-statements you made.

To add the things up though: Let's say you intend to read from address zero, so you do `char* ptr = 0; something(*ptr);`. C standard would allow this to set ptr to 0xffff, and reading from that address starts the motor. The C standard doesn't say. It just says that assigning 0 sets it to null pointer, which on some systems is 0xffff.

I've certainly worked on embedded stuff that "did stuff" when an address was read. Sometimes because nobody hooked up the R/W pin, because why would they if the address goes to a motor where "read" doesn't mean anything anyway?

Post reply on HN