175 points by justmarc 1 week ago | 115 comments
sylware 1 week ago
We need a C- ore µC:
No implicit cast except for literals and void* (explicit compile time/runtime casts), one loop statement (loop{}), no switch/enum/generic/_thread/typeof/etc, no integer promotion, only sized primitive types (u64 s32 f32 etc...), no anonymous code block, real compiler hard/compile time constant declaration, many operators have to go (--,++, a?b:c, etc)... and everything I am forgetting right now (the dangerous struct pack attribute...). But we need inline keywords for memory barriers, atomics for modern hardware architecture programming.
wongarsu 1 week ago
glouwbug 1 week ago
bregma 1 week ago
butterisgood 1 week ago
accelbred 1 week ago
sylware 1 week ago
nick__m 1 week ago
sylware 1 week ago
You should be able to generate machine code without the need of any runtime, like you can with C.
mlugg 1 week ago
The only thing I can think of that you might be referring to is compiler-rt: if so, this is a thing in C too! It's just a small collection of implementations for operations the code generator wants to call into (e.g. memset, arithmetic for integers larger than CPU word size). Clang uses compiler-rt when compiling C code, and GCC's equivalent is libgcc. Nonetheless, Zig lets you disable it using `-fno-compiler-rt`, in which case you'll need to provide the relevant symbols yourself somehow.
sylware 1 week ago
mlugg 1 week ago
(For context, by the way, I'm on the Zig "core team"; I'm a notable contributor to the project.)
sylware 1 week ago
accelbred 5 days ago
butterisgood 5 days ago
mhandley 1 week ago
int array[10];
*(array+1) = 56;
array[2] = 4;
3[array] = 27;
The first two are obvious, but the third is also legal. It works because array indexing is just sugar for pointer arithmetic, so array[2]=4 is identical in meaning to *(array+2)=4. Therefore 3[array]=27 is identical to *(3+array)=27 and so is legal. But just because you can doesn't mean you should.macintux 1 week ago
https://www.goodreads.com/book/show/198207.Expert_C_Programm...
dualogy 1 week ago
> There is one other convention — sometimes we repeat a key point to emphasize it. In addition, we sometimes repeat a key point to emphasize it.
One more quote and I'll stop:
> ctime() converts its argument into local time, which will vary from GMT, depending on where you are. California, where this book was written, is eight hours behind London, and several years ahead
WalterBright 1 week ago
D doesn't have that bug!
In 44 years of C programming, I've never encountered a legitimate use for the 3rd. (Other than Obfuscated C, that is.))
WolfeReader 1 week ago
WalterBright 1 week ago
But I call it a bug because it has no use and just pointlessly confuses people.
im3w1l 1 week ago
WolfeReader 6 days ago
kragen 1 week ago
WolfeReader 6 days ago
mhandley 1 week ago
matheusmoreira 1 week ago
dzaima 1 week ago
mananaysiempre 1 week ago
wahern 1 week ago
> void foo(int a[m][m], int m)
Currently you can only do: > void foo(int m, int a[m][m])
The holy grail is being able to update the prototypes of functions like snprintf to something like: > int snprintf(char buf[bufsiz], size_t bufsiz, const char *, ...);
However, array pointer decay means that foo above is actually: > void foo(int (*a)[m], int m)
Likewise, the snprintf example above would be little different than the current definition.There's related syntax, like
> foo (int m, int a[static m])
But a is still just a pointer, and while it can help some static analyzers to detect mismatched buffer size arguments at the call site, the extent of the analysis is very limited as decay semantics effectively prevent tracing the propagation of buffer sizes across call chains, even statically.There's no active proposal at the moment to make it possible to pass VM arrays (or rather, array references) directly to functions--you can only pass pointers to VM array types. That actually works (sizeof *a == sizeof (int) * m when declaring int (*a)[m] in the prototype), but the code in the function body becomes very stilted with all the syntactical dereferencing--and it's just syntactical as the same code is generated for a function parameter of `int (*a)[m]` as for `int *a` (underneath it's the same pointer value rather than an extra level of memory indirection). There are older proposals but they all lost steam because there aren't any existing implementation examples in any major production C compilers. Without that ability, the value of forward declarations is greatly diminished. Because passing VM array types to functions already requires significant refactoring, most of the WG14 felt it wasn't worth the risk of adopting GCC's syntax when everybody could (and should?) just start declaring size parameters before their respective buffer parameters in new code.
uecker 1 week ago
And yes, for new APIs you could just change the order, but it does help also with legacy APIs. It does even when not using pointers to arrays: https://godbolt.org/z/TM5Mn95qK (I agree that new APIs should pass a pointer to a VLA).
(edited because I am agreeing with most of what you said)
mananaysiempre 1 week ago
I know that was a common opinion pre-C23, but it feels like the committee trying to reshape the world to their desires (and their designs). It's a longstanding convention that C APIs accept (address, length) pairs in that order. So changing that will already get you a score of -4 on the Hard to Misuse List[1], for "Follow common convention and you'll get it wrong". (The sole old exception in the standard is the signature of main(), but that's somewhat vindicated by the fact that nobody really needs to call main(); there is a new exception in the standard in the form of Meneide's conversion APIs[2], which I seriously dislike for that reason.)
The reason I was asking is that 'uecker said it was requested at the committee draft stage for C23 by some of the national standards orgs. That's already ancient history of course, but I hoped the idea itself was still alive, specifically because I don't want to end up in the world where half of C APIs are (address, length) and half are (length, address), when the former is one of the few C conventions most everyone agrees on currently.
[1] https://ozlabs.org/~rusty/index.cgi/tech/2008-04-01.html
[2] https://thephd.dev/_vendor/future_cxx/papers/C%20-%20Restart...
dfawcus 1 week ago
$ make texe
cc -g -O2 -std=c11 -Wall -Wextra -Wpedantic -Werror -c -o test.o test.c
test.c: In function ‘do_test_formatSmallElem’:
test.c:108:9: error: ‘matSmallElemFormat’ accessing 8 bytes in a region of size 2 [-Werror=stringop-overflow=]
108 | matSmallElemFormat(elem, buffer);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.c:108:9: note: referencing argument 2 of type ‘char *’
In file included from test.c:8:
mat/display.h:17:6: note: in a call to function ‘matSmallElemFormat’
17 | void matSmallElemFormat(mElem elem, char buffer[static matSmallElemLen]);
| ^~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make: *** [<builtin>: test.o] Error 1
Gibbon1 1 week ago
struct foo
{
size_t elements;
int data[];
};
foo foo123 = {.elements = array_size(data), .data = {1, 2, 3}};
or
struct str
{
size_t sz;
char str[];
}; str s123 = {.sz = strlen(.str), .str = "123"};
uecker 1 week ago
dfawcus 1 week ago
The obvious one is rather than a function pointer typedef, such the subsequent use in a struct is obviously a pointer. Which helps when others are initially reading unfamiliar structures.
typedef int handler_ty(int a);
struct foo {
handler_ty *handler;
/* ... */
}
struct foo table[] = { { /* init fields */, /* init fields */, };
The other case can be somewhat related, namely as an assertion / check when writing such handler functions, and more importantly updating them. handler_ty some_handler;
int some_handler(int a) { /* ... */ }
When updating code, it allowed for easier to decode compiler errors if the expected type of handler_ty was changed, and some specific handler was incorrectly updated, or not updated at all.Basically the error would generally directly call out the inconsistency with the prior line, rather than with the distanct use in the initialisation of 'table'.
As I recall this mechanism has been around since at least C89, I don't recall using it in K&R.