Live data from Hacker News

Some C habits I employ for the modern day

unix.dog

131–140 of 162 posts

Re: Some C habits I employ for the modern day

#131

Earlier quoted context omitted.

Even simpler, you can do something like this to have length-delimited AND null-terminated strings (written from memory, no guarantees of correctness etc.): char *lenstrdup(char *s) { int n = strlen(s); char *p = malloc(n + sizeof(int) + 1); if(p) { strcpy(p + sizeof(int), s); *(int*)p = n; p += sizeof(int); } return p; } void lenstrfree(char *s) { free(s-sizeof(int)); }

One of the advantages to the pointer + length approach is free substrings. This inline approach doesn't allow that.

The ability to slice substrings results in a massive speed increase for string handling.

Re: Some C habits I employ for the modern day

#132

Earlier quoted context omitted.

> the fatal error was not combining the array dimension with the array pointer; all it needs is a little new syntax a[...]; this won’t fix any existing code. Over time, the syntax a[] can be deprecated by convention and by compilers. You're thinking in decades. C standard committee is slower than that. This could have worked in practice, but probably never will happen in practice. Maybe people should start considerin…

There is some irony in someone replying to the author of the D language suggesting that maybe the D language is the real solution he's looking for.

> maybe the D language is the real solution he's looking for

Yes, I realized that after not finding any 'droids.

Re: Some C habits I employ for the modern day

#133

Earlier quoted context omitted.

> the fatal error was not combining the array dimension with the array pointer; all it needs is a little new syntax a[...]; this won’t fix any existing code. Over time, the syntax a[] can be deprecated by convention and by compilers. You're thinking in decades. C standard committee is slower than that. This could have worked in practice, but probably never will happen in practice. Maybe people should start considerin…

As I see it, the problem with languages trying to replace C is that they not only try to fix fundamental flaws, but feel compelled to add unneeded features and break C's simplicity.

Also they leave a important point of C behind: backward-compatibility.

Re: Some C habits I employ for the modern day

#134

I'm a huge fan of the 'parse, don't validate' idiom, but it feels like a bit of a hurdle to use it in C - in order to really encapsulate and avoid errors, you'd need to use opaque pointers to hidden types, which requires the use of malloc (or an object pool per-type or some other scaffolding, that would get quite repetitive after a while, but I digress). You basically have to trade performance for correctness, wherea…

If you don't want your types to be public, don't put them in the public interface, put them into the implementation.

Re: Some C habits I employ for the modern day

#135

#if CHAR_BIT != 8 #error "CHAR_BIT != 8" #endif In modern C you can use static_assert to make this a bit nicer. static_assert(CHAR_BIT == 8, "CHAR_BIT is not 8"); ...although it would be a bit of a shame IMHO to add that reflexively in code that doesn't necessarily require it. https://en.cppreference.com/w/c/language/_Static_assert.html

Even if the code might not end up requiring it, if you write it with the assumption that bytes are 8 bits, it's good to document that with a static assert so someone porting things knows there will be dragons It's a pretty neat way to drop some corner cases from your mental load without building subtle traps

That's pretty silly IMHO, it should be incredibly obvious to anybody who is ever in a position to port code to a machine with non-8-bit-bytes that there will be dragons there. It also requires including limit.h which you might not otherwise need.

It's just not a realistic edge case, the machines like this are either antiquated or are tiny microcontrollers that can't practically run a POSIX OS. Very little code in the real world is generic enough to be useful in that environment (a good example might be a fixed point signal processing library).

There is no assertion in the entire Linux kernel that CHAR_BIT is eight, despite that assumption being hardcoded in many places.

Re: Some C habits I employ for the modern day

#136

> In the absence of proper language support, “sum types” are just structs with discipline. With enough compiler support they could be more than that. For example, I submitted a tagged union analysis feature request to gcc and clang, and someone generalized it into a guard builtin. https://github.com/llvm/llvm-project/issues/74205 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112840 GCC proved to be too complex for me…

It is on my list (also as a proposal to WG14). Sorry, I am a bit too overloaded currently. (If people want to help with such improvements - with either time or money, let me know.).

Re: Some C habits I employ for the modern day

#137

Earlier quoted context omitted.

> 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 :)

alloca is certainly worse. Worst-case fixed size array on the stack are also worse. If you need variable-sized array on the stack, VLAs are the best alternative. Also many other languages such as Ada have them.

Re: Some C habits I employ for the modern day

#138
post #92

Earlier quoted context omitted.

> Internet is full of people asserting CVEs in C are only caused by not skilled enough devs. Sure, but those people are not here, and usually aren't on HN anyway. The internet is also full of people asserting that CVEs in C++ are only caused by not skilled enough devs, but I consider those people irrelevant too. The reasons for rejecting C++ in this forum have been repeated often enough that you should have seen them…

Yeah the same reasons as the flamewars on comp.lang.c and comp.lang.c++. WG21 could do a better job, but that least they acknowledge security has to be tackled somehow.

Your implied claim that WG14 doesn't is incorrect, as you have been told before.

Re: Some C habits I employ for the modern day

#139
post #61

Earlier quoted context omitted.

> and you've gone to the trouble of refusing to accept CHAR_BIT!=8 This one was a head-scratcher for me. Yeah, there's no cost to check for it, but architectures where CHAR_BIT != 8 are rarer even than 24-bit architectures.

I got the impression the author was implying because CHAR_BIT is enforced to be 8 that uint8_t and char are therefore equivalent, but they are different types with very different rules. E.g. `char p = (char )&astruct` may violate strict aliasing but `uint8_t p = (uint8_t )&astruct` is guaranteed legal. Then modulo, traps, padding, overflow, promotion, etc.

It's the other way around.

Re: Some C habits I employ for the modern day

#140

Earlier quoted context omitted.

D is an elegant re-imagine of C and C++. For a trivial example, typedef struct S { int a; } S; becomes simply: struct S { int a; } and unlike C: extern int foo(); int bar() { return foo(); } int foo() { return 6; } you have: int bar() { return foo(); } int foo() { return 6; } For more complex things: #include becomes: import foo;

Your first example doesn't make sense, because struct S { int a; }; is also fine and idiomatic in C. It is rather typedef struct S { int a; } S; that doesn't make sense, because why would you make something opaque and expose it immediately again in the same line? The others are ... different. I can't tell whether they are really better. The second maybe, although I like it that the compiler forces me to forward type…

> is also fine and idiomatic in C

It's inelegant because without the typedef, you need to prefix it always with `struct`. This is inelegant because all other types do not need a prefix. It also makes it clumsier to refactor the code (adding or subtracting the leading `struct`). The typedef workaround is extremely commonplace.

> I like it that the compiler forces me to forward type stuff, it makes the code much more readable

That means when opening a file, you see the first part of the file first. In C, then you see a list of forward references. This isn't what you want to see - you want to see first the public interface, not the implementation details. (This is called "above the fold", coming from what you see in a folded stack of newspapers for sale. The headlines are not hidden below the fold or in the back pages.) In C, the effect of the forward reference problem is that people tend to organize the code backwards, with the private leaf functions first and the public functions last.

> include vs import is no difference

Oh, there is a looong list of kludgy problems stemming from a separate macro processor that is a completely distinct language from C. Even the expressions in a macro follow different rules than in C. If you've ever used a language with modules, you'll never want to go back to #include!

> What do you do when your file contains spaces?

A very good question! The module names must match the filename, and so D filenames must conform to D's idea of what an identifier is. It sounds like a limitation, but in practice, why would one want a module name different from its filename? I can't recall anyone having a problem with it. BTW, you can write:

    import core.stdc.stdio;
and it will look up `core/stdc/stdio.d` (Linux, etc.) or `core\stdc\stdio.d` on Windows.
Post reply on HN