Live data from Hacker News

Some C habits I employ for the modern day

unix.dog

111–120 of 162 posts

Re: Some C habits I employ for the modern day

#111

Earlier quoted context omitted.

I've been looking into Ada recently and it has cool safety mechanisms to encourage this same kind of thing. It even allows you to dynamically allocate on the stack for many cases.

You can allocate dynamically on the stack in C as well. Every compiler will give you some form of alloca().

True, but in many environments where C is used the stacks may be configured with small sizes and without the possibility of being grown dynamically.

In such environments, it may be needed to estimate the maximum stack usage and configure big enough stacks, if possible.

Having to estimate maximum memory usage is the same constraint when allocating a static array as a work area, then using a custom allocator to provide memory when needed.

Re: Some C habits I employ for the modern day

#112
post #18

That made me smile If I find myself needing a bunch of dynamic memory allocations and lifetime management, I will simply start using another language–usually rust or C#. Now that is some C habit for the modern day... But huh, not C.

My go to language for that is lua. I'm starting to think of it as a C framework more so than its own language.

Re: Some C habits I employ for the modern day

#113
post #86

Earlier quoted context omitted.

> Meanwhile after UNIX was done at AT&T, the C language authors hardly cared for the C standard committee in regards to the C compiler supported features used in Plan 9 and Inferno, being only "mostly" compatible, followed up having a authoring role in Alef, Limbo and Go. > I doubt most C advocates ever reflect on this. What would be the conclusion of this reflection? Assuming you have reflected on this, what was you…

That the language authors concluded C was done, there was no point collaborating with WG14, and there were better tools to do their operating systems research on.

> there were better tools to do their operating systems research on.

I think that's the key, Ritchie, Thompson, Pike were interested in OS research while people that love C today just want a simple and powerful language with manual memory management. It is not the first time in history when the creation has a separate life from the creator's wishes.

Re: Some C habits I employ for the modern day

#114
post #73

Earlier quoted context omitted.

> So you use strlen() a lot and don't have to deal with multibyte characters anywhere in your code. It's not much of a strategy. You don't need to support all multibyte encodings (i.e. DBCS, UCS-2, UCS-4, UTF-16 or UTF-32) characters if you're able to normalise all input to UTF-8. I think, when you are building a system, restricting all (human language) input to be UTF-8 is a fair and reasonable design decision, and…

Am I missing something here? UTF8 has multibyte characters, they're just spread across multiple bytes. When you strlen() a UTF8 string, you don't get the length of the string, but instead the size in bytes. Same with indices. If you Index at [1] in a string with a flag emoji, you don't get a valid UTF8 code point, but instead some part of the flag emoji. This applies with any UTF8 code points larger than 1 byte, whic…

Turns out that I rarely need to know sizes or indices of a UTF8 string in anything other than bytes.

If I write a parser for instance, usually, what to know is "what is the sequence of byte between this sequence of bytes and that sequence of bytes". That there are flag emojis or whatever in there don't matter, and the way UTF8 works ensures that a character representation doesn't partially overlap with a another.

What the byte sequences mean only really matters if you are writing an editor, so that you know how many bytes to remove when you press backspace for instance.

Truncation as to prevent buffer overflow seems to be a case where it would matter but not really. An overflow is an error and should be treated as such. Truncation is a safety mechanism, for when having your string truncated is a lesser evil. At that point, having half a flag emoji doesn't really matter.

Re: Some C habits I employ for the modern day

#115

Earlier quoted context omitted.

I've been looking into Ada recently and it has cool safety mechanisms to encourage this same kind of thing. It even allows you to dynamically allocate on the stack for many cases.

You can allocate dynamically on the stack in C as well. Every compiler will give you some form of alloca().

> You can allocate dynamically on the stack in C as well. Every compiler will give you some form of alloca().

And if it doesn't, VLAs are still in there until C23, IIRC.

Re: Some C habits I employ for the modern day

#116
post #17

If you really insist on not having a distinction between "u8"/"i8" and "unsigned char"/"signed char", and you've gone to the trouble of refusing to accept CHAR_BIT!=8, I'm pretty sure it'd be safer to typedef unsigned char u8 and typedef signed char i8. uint8_t/int8_t are not necessarily character types (see 6.2.5.20 and 7.22.1.1) and there are ramifications (see, e.g., 6.2.6.1, 6.3.2.3, 6.5.1).

Could you clarify an example of the ramifications? I tried looking through the C2Y standard draft to figure it out, but it's too complicated for me.

With the disclaimer that I let my language lawyer qualification lapse a while ago, it's broadly to do with the character types being the only approved way to examine the bytes of an object. An object of a type can be accessed only as if it were an object of that type or some compatible type, but: it can also be accessed as a sequence of characters. (You'd do this if implementing memcpy, memset or memcmp, for example.)

6.2.6.1 - only character types can be used to inspect the sequence of bytes making up an objuect, and (interestingly) only an array of unsigned char is suitable for memcpy'ing an object into for inspection. It's possible for sequences of bytes to exist that don't represent a valid value of the original object; it's undefined behaviour to read those sequences of bytes other than via a character type (i.e., I think, via a pointer to something compatible with the object's actual type - there being no other valid ways to even attempt to read it)

6.3.2.3 - when casting a pointer to an object type to a pointer to a character type, the new character pointer points to the bytes of the object. If converting between object types, on the other hand, the original pointer will (with care) round trip, and that seems to be all you can do, and actual access is not permitted

6.5.1 - as well as all the expected ways of accessing an object, objects can be accessed via a character pointer

Re: Some C habits I employ for the modern day

#117

Earlier quoted context omitted.

You can allocate dynamically on the stack in C as well. Every compiler will give you some form of alloca().

True, but in many environments where C is used the stacks may be configured with small sizes and without the possibility of being grown dynamically. In such environments, it may be needed to estimate the maximum stack usage and configure big enough stacks, if possible. Having to estimate maximum memory usage is the same constraint when allocating a static array as a work area, then using a custom allocator to provide…

Sure, the parent was commenting more about the capability existing in Ada in contrast to C. Ada variable length local variables are basically C alloca(). The interesting part in Ada is returning variable length types from functions and having them automatically managed via the “secondary stack”, which is a fixed size buffer in embedded/constrained environments. The compiler takes care of most of the dirty work for you.

We mainly use C++, not C, and we do this with polymorphic allocators. This is our main allocator for local stack:

https://bloomberg.github.io/bde-resources/doxygen/bde_api_pr...

… or this for supplying a large external static buffer:

https://bloomberg.github.io/bde-resources/doxygen/bde_api_pr...

Re: Some C habits I employ for the modern day

#118

> and I end up having all these typedefs in my projects I avoid doing this now. It's more trouble than it's worth and it changes your code from a standard dialect of C into a custom one. Plus my eyes are old and they don't enjoy separating short identifiers. > typedef struct { ... } String I avoid doing this. Just use `struct string { ... };'. It makes it clear what you're handling. C23 finally gave us "auto", you sh…

I was going to comment the same thing.

I had a coworker who had a very complicated set of "includes" that their code relied upon—not unlike the typedefs in the post. So his code was difficult to move around without also moving all his headers with it.

I try to minimize dependencies (custom headers, custom macros, etc.).

Re: Some C habits I employ for the modern day

#119

Earlier quoted context omitted.

You can allocate dynamically on the stack in C as well. Every compiler will give you some form of alloca().

> You can allocate dynamically on the stack in C as well. Every compiler will give you some form of alloca(). And if it doesn't, VLAs are still in there until C23, IIRC.

`-Wvla` Friends don’t let friends VLA :)

Re: Some C habits I employ for the modern day

#120

Earlier quoted context omitted.

The main thing I dislike about typedefs is that you can't forward declare them. If I know for sure I'm never going to need to do that then OK.

The usual solution for this is: typedef struct bla_s { ... } bla_t; Now you have a struct named 'bla_s' and a type alias 'bla_t'. For the forward declaration you'd use 'bla_s'. Using the same name also works just fine, since structs and type aliases live in different namespaces: typedef struct bla_t { ... } bla_t; ...also before that topic comes up again: the _t postfix is not reserved in the C standard :)

Yes, using the same Gtk example, the way you’d forward declare GtkLabel without including gtklabel.h in your header would be:

    struct _GtkLabel;
    typedef struct _GtkLabel GtkLabel;
    // Use GtkLabel* in declarations
Post reply on HN