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.
Some C habits I employ for the modern day
131–140 of 162 posts
Re: Some C habits I employ for the modern day
#132Earlier 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.
Yes, I realized that after not finding any 'droids.
Re: Some C habits I employ for the modern day
#133Earlier 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.
Re: Some C habits I employ for the modern day
#134I'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…
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
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…
Re: Some C habits I employ for the modern day
#137Earlier 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 :)
Re: Some C habits I employ for the modern day
#138Earlier 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.
Re: Some C habits I employ for the modern day
#139Earlier 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.
Re: Some C habits I employ for the modern day
#140Earlier 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…
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.