12 ms·
> Use `signed char` for strings If you're dealing with POSIX / the C stdlib, mirror it and use char* . (The string version of "Respect the integer types used b
by deathanatos 11y ago
> Use `signed char` for strings
If you're dealing with POSIX / the C stdlib, mirror it and use char* . (The string version of "Respect the integer types used by each API."; if you're coding for Windows you'll likely need to use LPWSTR or whatever they call it now.) But: if you're storing UTF-8 string data, you _want_ unsigned char (and maybe uint8_t[2]): if start trying to inspect the code units, esp. to decode them to code points, you're going to need to do comparisons and bit manipulations on them, and its too easy to write `code_unit < 0x80` or something similarly silly (e.g., while masking bits to see if it's a continuation byte). It's like the article early today called "Should I use Signed or Unsigned"[1] states: "Prefer unsigned when using bitwise operators".
> Limit recursion
Recursion is fine; you simply need to be aware of your worst case stack depth. You're looking, really, for the complexity of your stack usage, or the big-oh of your stack size. If you're doing something like recursing down an in-memory balanced binary tree, it's O(log(n)) in the worst case: ~64 stack frames on a 64-bit CPU. Can you fit that many on the stack? Certainly that's heavier than a normal function call, but it's still a finite, computable size.
If you can't compute that / prove an upper bound, then I agree.
> Aside: any language that uses keywords like public and private to determine visibility instead of placement/scoping is bad
If not with keywords, how should visibility be determined? (How does "placement/scoping" determine visibility?)
> Use get/init or create/copy/alloc in function names to indicate ownership versus borrowing for return values
Inevitable, I feel like you end up creating some structure, let's call it a Foo. So you then have a Foo * Foo_create(), a Foo_destroy(Foo * ). Some action performable on Foo? Foo_reticulate_splines(Foo* , …). Combined with,
> You should generally track used and unused space in dynamic arrays,
and the overhead of writing functionality to deal with dynamic arrays in C, the entire section about managing strings, and you've come to the very reason I left C: I'm already writing C++. Foo_create is a ctor; Foo_destroy a dtor; Foo_reticulate_splines a member function. Combined with vectors & strings taking care of day-to-day memory management, and I'm left with little reason to use C.[3] RAII adds some degree of object lifetime management that simply isn't present in C (but does not add the compile-time checks that I'm starting to love Rust for.)
[1]: http://blog.robertelder.org/signed-or-unsigned-part-2/ http://blog.robertelder.org/signed-or-unsigned-part-2/
[2]: I know C++ got some decent types for Unicode _very_ recently; I'm not aware if C got those.
[3]: I am not suggesting C++ to be the language you _should_ be using. I simply cannot justify ever using C over it.
- pjmlp 11y agoI never liked C. Back when I got to learn it, around 1993, I had already used Turbo Pascal 3, 5.5 and 6.0. C seemed so primitive and unsafe in regards to Turbo Pascal features. However the same person that got me into C, also showed me C++. There it was, a language that could be used as safely as Turbo Pascal, provided one made use of the language features instead of the C copy-paste compatibility. When Turbo Pascal stopped being a possibility, I joined ranks with the C++ side, in the C vs C++ flame wars. Even teached C++ at the university as a senior student. Only used C instead of C++, when asked to do so by university teachers or customers.