├── .gitignore ├── C.C ├── Makefile ├── README.md ├── auto_vararg.h ├── c.c ├── detention └── h.h ├── h.h ├── media ├── cursed_c_1.jpeg ├── cursed_c_1.mini.jpeg └── cursed_c_2.jpeg └── tool ├── clean_main.c ├── g().c └── g(...).c /.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | *.gch 3 | .gdb_history 4 | diff.txt 5 | -------------------------------------------------------------------------------- /C.C: -------------------------------------------------------------------------------- 1 | // check the extension 2 | int f(int, int, int, int) { 3 | return 1; 4 | } 5 | 6 | int main(){ 7 | 8 | } 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Add extra warnings to stress how horrid the code is 2 | WARNINGS := -Wall -Wextra -Wpedantic 3 | # Remove some that would show up too many times to be enjoyable 4 | WARNINGS += -Wno-duplicate-decl-specifier -Wno-trigraphs 5 | 6 | CFLAGS := ${WARNINGS} -fpermissive -trigraphs -ggdb 7 | 8 | main: h.h.gch 9 | gcc ${CFLAGS} c.c 10 | 11 | %.h.gch: detention/%.h 12 | gcc ${CFLAGS} $< -o ./$@ 13 | 14 | g: 15 | bake "tool/g().c" 16 | bake "tool/g(...).c" 17 | -diff -s -y -t <(objdump -S "tool/g().out") <(objdump -S "tool/g(...).out") > diff.txt 18 | 19 | clean: 20 | -rm *.gch 21 | -rm *.out 22 | -rm diff.txt 23 | -rm tool/*.out 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cursed C 2 | > An attempt to create the worst C program ever by repeatedly abusing obscure features. 3 | See `c.c` for the core code. 4 | Note that there were no version restraints imposed and 5 | extensions are also included. 6 | 7 | --- 8 | 9 | ![demo](media/cursed_c_1.mini.jpeg) 10 | 11 | --- 12 | 13 | ## Rationale 14 | + it was fun 15 | + perhaps it will be educational for someone 16 | 17 | 18 | ## Break down 19 | 20 | ### File naming 21 | `c.c` is the least descriptive name imaginable to man. 22 | It neither marks what project it belongs to or what purpose it has inside the project. 23 | 24 | ### Trigraph characters 25 | There used to be a time when there were computers widely in use 26 | without the ability to type some "normal" characters. 27 | So alternative representations were added to replace them. 28 | Basically built-in macros. 29 | Under `gcc` the `-trigraphs` flag is actually required to make them available. 30 | 31 | The ones relevant here are: 32 | ``` 33 | ??= : # 34 | ??< : { 35 | ??> : } 36 | ``` 37 | 38 | So for example `??=define` really is just equivalent to `#define`. 39 | 40 | ### \$\$\$ 41 | The dollar sign is valid to be the first (and only the first) char of a symbol name. 42 | My guess is that it is so for stubborn people 43 | who prefer their variable names to start with a '$' as it has to in some other languages. 44 | Perhaps it's for ancient parsers/syntax highlighters? 45 | Create an issue if you know better. 46 | 47 | ### Concatenation macro operator 48 | `##` is a special operator available inside macro definitions. 49 | It will literally glue together the arguments on its 2 sides. 50 | Historically it has been used to generate symbol names. 51 | 52 | ### include\_next 53 | Including is surprisingly high-level with search paths and precedence. 54 | Turns out this happens to open a hole in functionality, 55 | which is otherwise absent from C. 56 | 57 | So, 58 | `#include ` will start trying to include the file 'myheader.h' 59 | searching for in whatever directories the linker considers to contain system header files. 60 | Then, 61 | `#include "myheader.h"` 62 | start from the current directory, 63 | but falls back to the `<>` behavior. 64 | In either case, 65 | whatever is found first to satisfy the file name is taken. 66 | Now, what happens if we do not want the first? 67 | Or rather, we must have another. 68 | 69 | As the GNU documentation explains it through an example, 70 | say you wish to override a system header -say wrap it, 71 | but your own header depends on the original one. 72 | Standard C provides no way to resolve the situation. 73 | 74 | For this reason `#include_next` was added as a widely accepted extension, 75 | which excludes the current path from the list of searchable paths 76 | to prohibit recursive inclusion. 77 | 78 | ### iso646.h 79 | From what I've seen, 80 | this perfectly standard header that tends to be relatively little known. 81 | If you know C++ and about its keyword operators, 82 | then this is what defines their equivalents in C. 83 | E.g `and`, `or`, etc. 84 | 85 | Later down it's abused to get the address of `ű`: 86 | ```C++ 87 | bitand ű 88 | ``` 89 | Where -you guessed it- 90 | `bitand` is just macrod to be `&` at the end of the day. 91 | 92 | To make it extra fitting, it uses the worst imaginable formatting, 93 | which is -fun fact- unreproducible with '<>'s as the preprocessor '""'s 94 | just so happens to use the exact grammar as fully featured C string literals do. 95 | Lucky us, every '<>' include is a valid '""' include with only a slight compile overhead 96 | (as the local include is path is checked first, then the system headers). 97 | 98 | ### \_Decimal32 99 | It's exactly what it sounds like, 100 | a decimal fraction available in C. 101 | Here, as a GNU extension. 102 | 103 | ### Dot dot dot 104 | Not nearly as obscure as the rest, but worth including. 105 | `...` signals to the compiler that any number of values may be pushed into the stack 106 | on any call to this function. 107 | Normally `stdarg.h` and it's va\_list ("Variable Argument LIST") would be used or 108 | some pointer magic applied by hand to read said values, 109 | but here they are just left unused. 110 | 111 | The most famous function using this feature has to be `printf`. 112 | In glibc it's defined as: 113 | ```C 114 | extern int printf (const char *__restrict __format, ...); 115 | ``` 116 | That's why it can handle both`printf("%d", 1)` and `printf("%d %d", 1, 2)`. 117 | 118 | ### const const const 119 | The specifier `const` turns out to be infinitely stackable. 120 | In fact, it can be applied to the wrong spot too. 121 | 122 | ```C 123 | const const int i; 124 | ``` 125 | Reduces to: 126 | ```C 127 | const int i; 128 | ``` 129 | 130 | This program makes both the pointed value and the pointer constant, 131 | then reiterates it a few times for good measure. 132 | 133 | With a bit of simplification: 134 | ```C 135 | const char const * const; 136 | // ^ ^ ^ 137 | // | | | 138 | // | | constant pointer 139 | // | | 140 | // | const... air? for no effect 141 | // | 142 | // constant character value 143 | ``` 144 | 145 | ### Naming 146 | Having multi-char long variable names is bloat. 147 | Naming everything a single letter is the way of the warrior. 148 | #### Ű 149 | 'ű' is letter in the Hungarian alphabet. 150 | The reason it was chosen is because it is non-ascii. 151 | Don't like it? 152 | Perhaps switch it out for a '😁', 153 | which is also valid due to the same mechanic. 154 | 155 | ### Haskel style semi-colons 156 | I guess it's self-evident that C being whitespace agnostic, 157 | starting every line with ';' instead of closing them with it, 158 | is valid, 159 | however it's a rarely thought about possibility. 160 | Also, semi-colons are stackable. 161 | 162 | **Pro-tip:** the next time someone proposes indenting with spaces to you, 163 | suggest indenting with semi-colons. 164 | 165 | ### 'a' array 166 | When accessing an array element, 167 | `a[n]` is equivalent to `&a + n`. 168 | Note that the address is taken only figuratively, 169 | the array `a` decays to a pointer to the first element, 170 | so the more scientific notation is `*(a + n)` 171 | #### Smiles 172 | Like trigraphs, but they are too hard to type? Digraphs are for you! :> 173 | ``` 174 | %: : # 175 | <% : { 176 | %> : } 177 | <: : [ 178 | :> : ] 179 | ``` 180 | #### Reverse reference 181 | ```C 182 | (-1)[$a]; 183 | ``` 184 | No, we are not trying to access the `$a`th element of `-1`. 185 | Addition commutative, hence `*(a + n)` == `*(n + a)`. 186 | 187 | The above evaluates exactly like: 188 | ```C 189 | $a[-1]; 190 | ``` 191 | 192 | But watch out for the operator precedence, 193 | because `-1[$a]` would be the negative value of element 1. 194 | 195 | #### Negative index 196 | Since `a + (-n)` is `a - n`, 197 | it just werks™. 198 | 199 | ### Puts call 200 | Perhaps you noticed `#include ` missing. 201 | Don't worry, 202 | the compiler will implicitly handle it for us! 203 | 204 | ### Return 205 | Relative to Assembly, C is high-level. 206 | And in many cases, 207 | for Assembly programmers, 208 | the `return` statement high-level enough to be considered many ways similar to a function. 209 | For this reason they may stick to the following syntax: 210 | ```C 211 | return(0); 212 | ``` 213 | 214 | ### END comments 215 | Large blocks being closed with their type/name explicitly mentioned 216 | tend to be more readable than otherwise. 217 | With C however a nice balance of size is attainable. 218 | Overshoot a bit and you get people pretending comments are significant. 219 | Wait 30 years and -looking for a more radical solution- 220 | they form a cult around DRY. 221 | 222 | ### Attributes 223 | Attributes tend to be left out from textbooks, 224 | but they are extremely cool. 225 | They provide ways to both aid the compiler (or make it shut up) 226 | and make code self-documenting. 227 | 228 | Too bad that C initially botched the syntax. 229 | Consider the following: 230 | 231 | Both of these specify that `func` will never return. 232 | 233 | C: 234 | ```C 235 | __attribute__((noreturn)) 236 | ``` 237 | C++: 238 | ```C++ 239 | [[ noreturn ]] 240 | ``` 241 | 242 | But good news! 243 | With C23, 244 | C++ style attributes are being introduced. 245 | 246 | What better way to celebrate this, 247 | than to use all syntaxes possible at the same time? 248 | 249 | ### F == F 250 | As unreadable the code might be, 251 | surely, 252 | this line should prematurely terminate our horrors, right? 253 | ```C 254 | if (F == F) { return 1; } 255 | ``` 256 | No, you see, because floats. 257 | This, 258 | technically, 259 | has little to do with C. 260 | IEEE 754, 261 | in a round about way, 262 | specifies that comparisons with NaN is false. 263 | This extends to `NaN == NaN` too. 264 | C simply implements it. 265 | 266 | And, 267 | thanks to the dubious operation of zero divided by zero, 268 | used for defining `F`, 269 | resulting in NaN, 270 | we are able to proceed to the rest of the mayhem. 271 | 272 | ### Google? 273 | Google. 274 | ```C 275 | ; https://google.com 276 | ``` 277 | URLs happen to be valid C. 278 | More precisely, 279 | they are -due to pure chance- a label plus a comment. 280 | You can only have one per protocol per function, 281 | choose wisely. 282 | 283 | The address was chosen in particular, 284 | because hard coding google endpoints into source is as evil 285 | (or rather, no longer "don't be evil") 286 | as it gets. 287 | 288 | ### ? 289 | If one asks a C programmer 290 | "can the ternary operator be used to conditionally declare a (const) variable" 291 | even if he never needed to use it, 292 | he will instinctively answer yes. 293 | Yet, 294 | no one seems to ever state this -truth be told, rare times, but- very useful feature. 295 | Instead, its main purpose is to serve as premature formatting. 296 | 297 | While the functionality is not cursed, 298 | the poor operator itself seems to be. 299 | 300 | ### Nested functions 301 | You would be surprised to know how popular these things used to be in the past. 302 | And I'm not talking about C in particular. 303 | 304 | > In the case of nested procedures, the scope rules permit, for example, the definition 305 | > of a procedure to include the definition of subsidiary procedures without concern for 306 | > interference with other global procedures. This capability facilitates writing pro- 307 | > cedures and often simplifies their usage. 308 | > 309 | > -- David R. Hanson; Is Block Structure Necessary? (1981) 310 | 311 | Yet, 312 | they have fallen out from the public's taste so much that C++ disallows it. 313 | Nested classes seem to be looked down upon too now days, 314 | even in bloody Java tutorials where providing a single file would 315 | be so damn much clearer and easier, 316 | but I digress. 317 | 318 | ### Code in switch 319 | A switch statement is just a fancy code block with a bunch of labels and a jump-table. 320 | So, in our: 321 | ```C 322 | switch (h) { 323 | int a = 89; 324 | default: break; 325 | } 326 | ``` 327 | expression, 328 | the available value mappings (none) are compared to h, 329 | then execution jumps to `default` as no match was found. 330 | Now the question becomes: 331 | how is that `a` is defined to 89? 332 | Easy. It's not. 333 | `a` gets defined, but its assignment is skipped. 334 | It's because variable declarations always result in allocations at the start of the scope, 335 | no further questions asked. 336 | For all the compiler cares, 337 | `int a;` might as well have been written under `default:`. 338 | However, the value copy is skipped. 339 | 340 | ### String literal concatenation 341 | Adjacent string literals are always (compile time) concatenated. 342 | Therefore: 343 | ```C 344 | "Make it stop, cruel" " " "w" "o" "r" "l" "d" "!" 345 | ``` 346 | Costlessly becomes: 347 | ```C 348 | "Make it stop, cruel world!" 349 | ``` 350 | Why is it cursed? Ask who ever does this: 351 | ```C 352 | puts("Message line 1"); 353 | puts("Message line 2"); 354 | ``` 355 | Instead of: 356 | ```C 357 | puts("Message line 1\n" 358 | "Message line 2"); 359 | ``` 360 | 361 | ### Literal pointer arithmetics 362 | A string literal "decays" into a const char pointer, 363 | therefore `puts("//Please!" + 2)` is just the heretic way of saying: 364 | ```C 365 | const char * s = "//Please!"; 366 | puts(s + 2); 367 | ``` 368 | 369 | ### Batch assignment 370 | The assignment operator is not particularly "special" in C. 371 | It does the actual value copying, 372 | but then plainly "returns" a value of its own. 373 | Which happens to be defined as the one assigned. 374 | 375 | "Tricks" like this and such: 376 | ```C 377 | do { 378 | // ... 379 | } while(i = fun()); 380 | ``` 381 | tend to be pretty neat. 382 | 383 | However, 384 | for wholeness, 385 | it stacks. 386 | ```C 387 | i = h = j = k 388 | ``` 389 | Is evaluated right to left. 390 | I.e. it becomes "assign k to j; assign j to h; assign h to i;". 391 | So what value does `i` hold after the operation? 392 | No one knows, since `k` was never initialized! 393 | 394 | ### Comma operator 395 | The comma operator evaluates the expressions on both sides, 396 | then returns the value returned by the right-hand side. 397 | Again, it's stackable. 398 | 399 | This -similar to the wise usage of assignments,- 400 | opens a little door for playing with loop conditions. 401 | ```C 402 | do { 403 | // ... 404 | } while((fun(&a), a)); 405 | ``` 406 | 407 | 408 | But also, if you ever wanted to make semi-colons jealous to make them love you more, 409 | here is your chance. 410 | ```C 411 | fun1(), fun2(), fun3(), fun4() 412 | ``` 413 | 414 | > Do not use the comma operator. 415 | > 416 | > Do not use the comma operator. 417 | > 418 | > Do not use the comma operator. 419 | > 420 | > [...] 421 | > 422 | > Do not use the comma operator. 423 | > 424 | > Do not use the comma operator. 425 | > 426 | > Do not use the comma operator. 427 | > 428 | > -- Repeater, https://cplusplus.com/forum/beginner/272615/ 429 | 430 | ### K&R arguments 431 | The language design of C is truly genius. 432 | Brian Kernighan and Dennis Ritchie got so many things perfectly right. 433 | One clear exception is their original function declaration syntax. 434 | Not that it doesn't make sense or does not have historical reasons, 435 | but damn is it to ugly and confusing. 436 | 437 | A reasonably cleaned up version of our `main()` would look like this: 438 | ```C 439 | void main(argc, argv, envp) 440 | int argc; 441 | char * * argv; 442 | char * * envp; 443 | { ; } 444 | ``` 445 | Where our parameters are named first, 446 | then their type is specified. 447 | This original way, 448 | used to be the only way to do it. 449 | It may look like absolutely useless typing at first, 450 | but there are a few things to consider. 451 | 452 | 1. it roots from B syntax, 453 | which is typeless: 454 | ```B 455 | main(argc, argv, envp) { 456 | ; 457 | } 458 | ``` 459 | 460 | 2. One doesn't specify the types of your arguments 461 | until the function definition: 462 | ```C 463 | 464 | void main(argc, argv, envp); 465 | 466 | void main(argc, argv, envp) 467 | char * * argv; 468 | int argc; 469 | char * * envp; 470 | { ; } 471 | ``` 472 | Which implies that 473 | a) you don't have to keep your declarations and definitions in sync 474 | b) calls can compile without type checking 475 | 476 | 3. It blends with other syntax rules, allowing this: 477 | ```C 478 | void main(argc, argv, envp) 479 | char * * argv; 480 | int argc; 481 | char * * envp; 482 | { ; } 483 | ``` 484 | And this: 485 | ```C 486 | void main(argc, argv, envp) 487 | int argc; 488 | char * * argv, * * envp; 489 | { ; } 490 | ``` 491 | 492 | ### I would like to declare the type of nothing 493 | ```C 494 | g() int; int; { int; } 495 | ``` 496 | GCC allows you have a typename on its own line. 497 | Not even a warning in sight. 498 | 499 | Why does it allow it? Good question. 500 | 501 | ### h.h 502 | As you know, this project compiles under gcc, 503 | Still, catting out h.h should make you raise your eyebrow. 504 | ```C 505 | #error 506 | ``` 507 | Its impossible that that's the actual header included and 508 | I must have messed with the compiler, right? 509 | Well, yes, but not the way you think. 510 | If you wanted to prove me guilty of manipulating the include path 511 | by inspecting the build script, 512 | you could not: 513 | ```sh 514 | gcc c.c -trigraphs -Wall -Wextra -Wpedantic 515 | ``` 516 | Tho, there is also something suspicious going on with `h.h`: 517 | ```sh 518 | gcc detention/h.h -o ./h.h.gch 519 | ``` 520 | Where the contents of `detention/h.h` seems like something that would translate. 521 | That is a good lead, however `detention/` is hardly a standard folder to override with. 522 | 523 | The solution is in the `.gch` extension as in "GNU Compiled Header". 524 | Its what it sounds like, a header processed by gcc. 525 | After the header is compiled once, 526 | its code can be included in multiple source files without it being reprocessed. 527 | This trick can significantly speed up compile times. 528 | Well, when it works that is. 529 | 530 | For example if it was 1 line lower in the source, 531 | it would make the operation `#error` out. 532 | The documentation lists 10 reasons why a compiled header could be rejected 533 | and `iso646.h` breaks *one* of those. 534 | Considering that's a standard header with around a dozen defines, 535 | you can guess how stable it is. 536 | Not to mention it fails silently. 537 | gcc provides no feedback whether it did or did not use a compiled header, 538 | let alone why. 539 | Pessimism aside, 540 | compiled headers are great as long as you can make sure 541 | they are at the top of the source (with an `-include` perhaps) 542 | and seem way more reliable with clang (because they have their own version too). 543 | 544 | ### Empty parenthesis 545 | How would you declare a function with no parameters? 546 | If you answered the following: 547 | ```C 548 | f(); 549 | ``` 550 | Then you just found a footgun! 551 | That actually signals how the function takes... 552 | well, 553 | we don't know what it takes, so better allow anything! 554 | Take a look at `g`: 555 | ```C 556 | g() int; int; { int; } 557 | ``` 558 | Ignoring those `int`s, 559 | one could assume it takes 0 arguments, 560 | yet if you glimpse at how it's called: 561 | ```C 562 | g(i = h = j = k) 563 | ``` 564 | You will see how passing one int is valid, in fact, it doesn't even warrant a warning! 565 | This, 566 | again routes from the K&R style, 567 | where as established before, 568 | arguments where not expected to be type checked. 569 | 570 | Sooo, 571 | is `g()` equivalent to `g(...)`? 572 | No! 573 | For one, 574 | `...` required a named argument before itself until C2X. 575 | However, 576 | in the case of `g()` the compiler not required to perform type promotions. 577 | Also, 578 | the generated assembly is... strange. 579 | That's what `tool/g().c` and `tool/g(...).c` are here for. 580 | Here is the relevant part of their disassembly: 581 | ```Asm 582 | // @BAKE gcc -g -O0 "$@" -o "$*" | // @BAKE gcc -g -O0 "$@" -o "$*" -Wall -Wpedantic 583 | 584 | g() { | g(...) { 585 | 1129: f3 0f 1e fa endbr64 1129: f3 0f 1e fa endbr64 586 | 112d: 55 push %rbp 112d: 55 push %rbp 587 | 112e: 48 89 e5 mov %rsp,%rbp 112e: 48 89 e5 mov %rsp,%rbp 588 | > 1131: 48 83 ec 38 sub $0x38,%rsp 589 | > 1135: 48 89 bd 50 ff ff ff mov %rdi,-0xb0(%rbp) 590 | > 113c: 48 89 b5 58 ff ff ff mov %rsi,-0xa8(%rbp) 591 | > 1143: 48 89 95 60 ff ff ff mov %rdx,-0xa0(%rbp) 592 | > 114a: 48 89 8d 68 ff ff ff mov %rcx,-0x98(%rbp) 593 | > 1151: 4c 89 85 70 ff ff ff mov %r8,-0x90(%rbp) 594 | > 1158: 4c 89 8d 78 ff ff ff mov %r9,-0x88(%rbp) 595 | > 115f: 84 c0 test %al,%al 596 | > 1161: 74 20 je 1183 597 | > 1163: 0f 29 45 80 movaps %xmm0,-0x80(%rbp 598 | > 1167: 0f 29 4d 90 movaps %xmm1,-0x70(%rbp 599 | > 116b: 0f 29 55 a0 movaps %xmm2,-0x60(%rbp 600 | > 116f: 0f 29 5d b0 movaps %xmm3,-0x50(%rbp 601 | > 1173: 0f 29 65 c0 movaps %xmm4,-0x40(%rbp 602 | > 1177: 0f 29 6d d0 movaps %xmm5,-0x30(%rbp 603 | > 117b: 0f 29 75 e0 movaps %xmm6,-0x20(%rbp 604 | > 117f: 0f 29 7d f0 movaps %xmm7,-0x10(%rbp 605 | return 2; return 2; 606 | 1131: b8 02 00 00 00 mov $0x2,%eax | 1183: b8 02 00 00 00 mov $0x2,%eax 607 | } } 608 | 1136: 5d pop %rbp | 1188: c9 leave 609 | 1137: c3 ret | 1189: c3 ret 610 | ``` 611 | On the right side, 612 | `g(...)` actually reads a bunch of values from the stack into registers. 613 | What I *think* is happening is that the compiler would really like to optimize 614 | arguments being read from the stack directly, 615 | but since -O0 was on, 616 | it was not allowed to make any assumptions about, 617 | how `g` will be called, 618 | hence it loads as much as it can. 619 | 620 | (If you know better, please correct me!) 621 | 622 | ### Duff() 623 | As you can see `case` statements work inside nested blocks of the corresponding `switch`. 624 | Which is funny by itself, but what that allows us to do is something called a "Duff's Device". 625 | This allows us to force loop unrolling within the bounds of C. 626 | 627 | I can explain it best with an incremental example. Consider: 628 | ```C 629 | do { 630 | perform_stuff(); 631 | } while (counter++ < repeat_count); 632 | ``` 633 | Here, 634 | after every call to `perform_stuff()` we have to perform a jump instruction 635 | to the top of the loop for every repetition. 636 | With the standards of modern software, 637 | the cost is small, 638 | however there are circumstances where it could be a legitimate performance concern. 639 | An optimization would be: 640 | ```C 641 | do { 642 | perform_stuff(); 643 | perform_stuff(); 644 | perform_stuff(); 645 | perform_stuff(); 646 | counter += 4; 647 | } while (counter < repeat_count); 648 | ``` 649 | Now, we only need a jump after every 4 calls! 650 | Great, 651 | except if `repeat_count` is not a multiple of 4, 652 | we actually call `perform_stuff()` the wrong number of times... 653 | This is where Duff's Device comes into the picture. 654 | ```C 655 | switch (repeat_count % 4) { 656 | case 0: do { perform_stuff(); counter++; 657 | case 1: perform_stuff(); counter++; 658 | case 2: perform_stuff(); counter++; 659 | case 3: perform_stuff(); counter++; 660 | } while (counter < repeat_count); 661 | } 662 | ``` 663 | The switch "eats away" some number of calls, 664 | guaranteeing `(repeat_count - count) % 4 == 0` and 665 | `peform_stuff()` being called the desired number of times. 666 | (Unless it was 0, that's an edge case we do not account for in the example for simplicity.) 667 | In our `duff()` we apply this trick to copying a string. 668 | 669 | It should also be mentioned, 670 | that due to modern compiler optimizations, 671 | Duff's Device may or may not result in actual performance gains 672 | ([see here](https://belaycpp.com/2021/11/18/duffs-device-in-2021/)). 673 | You may read Duff's own thoughts regarding it 674 | [here](http://doc.cat-v.org/bell_labs/duffs_device). 675 | 676 | ### \_sum(int argc, ...) 677 | Back to variadic functions and their quirks. 678 | Who cares if they nuke type safety? 679 | Let's talk about the real problem: ergonomics. 680 | 681 | Any variadic function has to have some way to tell how many arguments it was given. 682 | In practice this boils down to one of the following techniques: 683 | + pass the number of extra arguments as a named argument 684 | + encode the number of arguments in a named argument (e.g. format string) 685 | + choose a termination value for the extra arguments (e.g `NULL`, passed as the last argument) 686 | 687 | Sometimes -when trying to create comfortable interfaces- the first option seems perfect, 688 | if not for passing this count by hand. 689 | 690 | Thing is, the preprocessor can save our day. 691 | Take a look inside `auto_vararg.h`. 692 | 693 | Macros can be variadic too. 694 | `__VA_ARGS__` is a standard macro, it simply expands to the variadic arguments. 695 | 696 | `PP_128TH_ARG` always expands to its 128th argument. 697 | 698 | `PP_RSEQ_N` expands to 128 numbers in descending order, 0 inclusive. 699 | 700 | If we pass `PP_RSEQ_N` to `PP_128TH_ARG` it will return 0 to us. 701 | If we were to place exactly one argument before `PP_RSEQ_N`, 702 | all of its values would be shifted to the right, 703 | resulting in `1` becoming the 128th argument of `PP_128TH_ARG`. 704 | More generally, passing N arguments before `PP_RSEQ_N` will offset it by N, 705 | shifting the correct value to the 128th argument of `PP_128TH_ARG`. 706 | 707 | In effect, we have just automated passing the argument count! 708 | 709 | Argument counter hack credits: 710 | + [https://stackoverflow.com/a/35693080](https://stackoverflow.com/a/35693080) 711 | + [https://stackoverflow.com/a/26408195](https://stackoverflow.com/a/26408195) 712 | + [https://stackoverflow.com/a/2124385](https://stackoverflow.com/a/2124385) 713 | 714 | --- 715 | 716 | ## Challenge 717 | + Try to make the project worse 718 | ### Completionists 719 | * SurmanPP 720 | * 8e12 721 | -------------------------------------------------------------------------------- /auto_vararg.h: -------------------------------------------------------------------------------- 1 | #ifndef AUTO_VARARG_H 2 | #define AUTO_VARARG_H 3 | 4 | #include 5 | 6 | #define PP_NARG(...) \ 7 | PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) 8 | #define PP_NARG_(...) \ 9 | PP_128TH_ARG(__VA_ARGS__) 10 | #define PP_128TH_ARG( \ 11 | _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 12 | _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ 13 | _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ 14 | _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ 15 | _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ 16 | _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ 17 | _61,_62,_63,_64,_65,_66,_67,_68,_69,_70, \ 18 | _71,_72,_73,_74,_75,_76,_77,_78,_79,_80, \ 19 | _81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \ 20 | _91,_92,_93,_94,_95,_96,_97,_98,_99,_100, \ 21 | _101,_102,_103,_104,_105,_106,_107,_108,_109,_110, \ 22 | _111,_112,_113,_114,_115,_116,_117,_118,_119,_120, \ 23 | _121,_122,_123,_124,_125,_126,_127,N,...) N 24 | #define PP_RSEQ_N() \ 25 | 127,126,125,124,123,122,121,120, \ 26 | 119,118,117,116,115,114,113,112,111,110, \ 27 | 109,108,107,106,105,104,103,102,101,100, \ 28 | 99,98,97,96,95,94,93,92,91,90, \ 29 | 89,88,87,86,85,84,83,82,81,80, \ 30 | 79,78,77,76,75,74,73,72,71,70, \ 31 | 69,68,67,66,65,64,63,62,61,60, \ 32 | 59,58,57,56,55,54,53,52,51,50, \ 33 | 49,48,47,46,45,44,43,42,41,40, \ 34 | 39,38,37,36,35,34,33,32,31,30, \ 35 | 29,28,27,26,25,24,23,22,21,20, \ 36 | 19,18,17,16,15,14,13,12,11,10, \ 37 | 9,8,7,6,5,4,3,2,1,0 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /c.c: -------------------------------------------------------------------------------- 1 | ??=define ARGUMENT_PREFIX(x) $arg ## x 2 | 3 | ??=include "h.h" 4 | ??=include "auto_vararg.h" 5 | ??=include_next "\ 6 | iso646.h\ 7 | " 8 | 9 | typedef 10 | const const const unsigned char 11 | const const * const 12 | const const * const 13 | const const string 14 | ; 15 | ű(_Decimal32 named_argument, ...) ??< \ 16 | ;;int $a<:0:> \ 17 | ;;puts("Let me segfault!") \ 18 | ;;return ((-1)[$a]) \ 19 | ;;??> // END ű 20 | 21 | g() int; int; { int; } 22 | 23 | duff() ??< 24 | char src[] = "Copy this message"; 25 | int n = sizeof(src); 26 | char dest[n]; 27 | int i = 0; 28 | int j = 0; 29 | switch (n % 4) { 30 | case 0: do { dest[i++] = src[j++]; 31 | case 1: dest[i++] = src[j++]; 32 | case 2: dest[i++] = src[j++]; 33 | case 3: dest[i++] = src[j++]; 34 | } while (j < n); 35 | } 36 | ??> 37 | 38 | #define sum(...) _sum(PP_NARG(__VA_ARGS__), __VA_ARGS__) 39 | _sum(int argc, ...) ??< 40 | int r = 0; 41 | va_list va; 42 | va_start(va, argc); 43 | for (int i = 0; i < argc; i++) { 44 | r += va_arg(va, int); 45 | } 46 | va_end(va); 47 | return r; 48 | ??> 49 | 50 | //fortran /* sadly gcc doesn't support it */ 51 | _Noreturn 52 | __attribute__((noreturn)) 53 | const void 54 | [[ noreturn ]] 55 | main 56 | ( 57 | ARGUMENT_PREFIX (c), 58 | ARGUMENT_PREFIX (v), 59 | $envp 60 | ) 61 | __int128 ARGUMENT_PREFIX (c); 62 | string ARGUMENT_PREFIX (v); 63 | string $envp; 64 | ??< 65 | signed f 66 | ( 67 | ) 68 | ??< 69 | ((const void (*)(void))bitand ű)(); 70 | switch (h) 71 | ??< 72 | int a = 89; 73 | default: 74 | break; 75 | ??> 76 | ??> /* END f */ 77 | ; float F = 0.0f / 0.0f 78 | ; if (F == F) { return 1; } 79 | ; long long i, h, j, k 80 | ; https://google.com 81 | ; const short d = i ? 2*i : i 82 | ; puts("Make it stop, cruel" " " "w" "o" "r" "l" "d" "!") 83 | , puts("//Please!" + 2) 84 | , f(g(i = h = j = k)) 85 | ; duff() 86 | ; sum(4, 8, 6, 12, 5) 87 | ; 88 | ??> /* END main */ 89 | -------------------------------------------------------------------------------- /detention/h.h: -------------------------------------------------------------------------------- 1 | int h = 42; 2 | -------------------------------------------------------------------------------- /h.h: -------------------------------------------------------------------------------- 1 | #error 2 | -------------------------------------------------------------------------------- /media/cursed_c_1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agvxov/cursed_c/536b7b3db5e3d52b4cc262fa9326b5cef54406bf/media/cursed_c_1.jpeg -------------------------------------------------------------------------------- /media/cursed_c_1.mini.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agvxov/cursed_c/536b7b3db5e3d52b4cc262fa9326b5cef54406bf/media/cursed_c_1.mini.jpeg -------------------------------------------------------------------------------- /media/cursed_c_2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agvxov/cursed_c/536b7b3db5e3d52b4cc262fa9326b5cef54406bf/media/cursed_c_2.jpeg -------------------------------------------------------------------------------- /tool/clean_main.c: -------------------------------------------------------------------------------- 1 | // @BAKE tcc $@ -o $*.out 2 | 3 | void main(argc, argv, envp); 4 | 5 | void main(argc, argv, envp) 6 | int argc; 7 | char * * envp, * * argv; 8 | { ; } 9 | -------------------------------------------------------------------------------- /tool/g().c: -------------------------------------------------------------------------------- 1 | // @BAKE gcc -g -O0 "$@" -o "$*.out" 2 | 3 | g() { 4 | return 2; 5 | } 6 | 7 | main () { 8 | g(4, 3, 2); 9 | } 10 | -------------------------------------------------------------------------------- /tool/g(...).c: -------------------------------------------------------------------------------- 1 | // @BAKE gcc -g -O0 "$@" -o "$*.out" -Wall -Wpedantic 2 | 3 | g(...) { 4 | return 2; 5 | } 6 | 7 | main () { 8 | g(4, 3, 2); 9 | } 10 | --------------------------------------------------------------------------------