16 ms·
Lesser known tricks, quirks and features of C
- zabzonk 4y agothe c training course at a popular uk training company (the instruction set) had duff's device on something like page 5 of their c course - expunging it was one of the first things i did when i joined them. there were many others.
- zabzonk 4y agoi don't ask this too often - but what is wrong with this comment?
- bombcar 4y agoI presume you angered the Duff Defenders (there are those that feel C shouldn’t be taught unless you’re taught all the weird things it can do).
- zabzonk 4y agoyes, that was probably my feeling at the time. but perhaps a bit much for people struggling with compiling and running hello world.
- gallier2 4y agoCool. Two of the tricks shown are from my contribution in stackoverflow.
- vocram 4y agoYet another proof that C is simple but not easy.
- SAI_Peregrinus 4y agoThe simplest languages tend to be the most difficult. Brainfuck, Binary Lambda Calculus, Unlambda, and other "Turing tarpits" are all extremely difficult to use for anything even mildly complex.
- elcritch 4y agoForth. It’s easy for a small cute script. But quickly becomes a PITA.
- gatane 4y agoStack machines look cool until you realize you could save all the stack mumbling by using registers. At this point, you could try to make a full VM instead.
- mtlmtlmtlmtl 4y agoI always felt like unlambda and other SKI calculus esque esolangs(iota comes to mind) could have some kinda strange use case in some kind of generalised genetic programming. It should be possible to create a binary notation for SKI calculus where arbitrary bitstrings will be valid, and so one could randomly mutate and recombine arbitrary programs. Though I've never delved deeper into genetic algorithms and evolutionary programming, my sense is that genetic algorithms tend to be restricted to parameterised algorithms where the "genes" determine the various parameters. Which can be great for optimisation problems. It's one of those weird ideas I've had kicking about for years but never did anything about, and yet I keep coming back to it.
- alcover 4y agoThis must be an avenue for very exciting explorations. I'm quite ignorant about this stuff but have some questions : > It should be possible to create a binary notation for SKI calculus where arbitrary bitstrings will be valid What if it's not ? How will your genetic petri dish spot and eliminate invalid programs ? > one could randomly mutate and recombine arbitrary programs What if non-halting programs get generated ? In this vein I've seen magnificent images of 1D cellular automatons that use the surrounding pattern to decide on the local rule for next gen.
- Miserlou57 4y ago“Quirks and features”
- aranchelk 4y agoWhere’s the DougScore?
- dantle 4y agoNice article. Saw a few things I wish I'd known about. 1. %n in printf would be handy when writing CLIs dealing w/ multiple lines or precise counts of backspaces. 2. Using enums as a form of static_assert() is a great idea (triggering a div by zero compiler error).
- jcelerier 4y agousing enums as a form of static_assert is very bad when C nowadays literally has static_assert (_Static_assert: https://gcc.godbolt.org/z/bfv6rKdKM https://gcc.godbolt.org/z/bfv6rKdKM)
- torstenvl 4y ago%n is an extremely poor fit for CLI manipulation or tokenization for backspacing. %n is for bytes, not user-perceived characters.
- tom_ 4y agoThe enum idea is interesting. I've previously used an extern with a conditional size of either 1 (valid) or -1 (invalid). This requires no additional boilerplate, and is #define-able into a static assert when built with a recent enough compiler. Something like this, from memory: #define STATIC_ASSERT(COND) extern char static_assert_cond_[(COND)?1:-1] /* C99 or earlier */ #define STATIC_ASSERT(COND) _Static_assert(COND) /* C11 or later */ As both are declarations, I don't think you'll end up in a situation where one is valid and the other isn't - but I could be wrong, and I suspect it would rarely matter in practice anyway.
- ufo 4y agoFun fact about %n: Mazda cars used to have a bug where they used printf(str) instead of printf("%s", str) and their media system would crash if you tried to play the "99% Invisible" podcast in them. All because the "% In" was parsed as a "%n" with some extra modifiers. https://99percentinvisible.org/episode/the-roman-mars-mazda-virus/ https://99percentinvisible.org/episode/the-roman-mars-mazda-...
- rerdavies 4y agoFun fact about %n: The %n functionality also makes printf accidentally Turing-complete even with a well-formed set of arguments. A game of tic-tac-toe written in the format string is a winner of the 27th IOCCC. - sez wiki. A not so fun fact: Because the %n format is inherently insecure, it's disabled by default. - MSVC reference.
- charcircuit 4y ago>The %n functionality also makes printf accidentally Turing-complete No it doesn't. Printf has no way to loop so it's not Turing complete. Even if you did what the IOCCC entry did with putting it into a loop it still wouldn't be Turing complete as it would not have an infinite memory.
- webstrand 4y agoNothing real is "Turing complete" if it requires infinite memory. That's a property only abstract machines can have. In common parlance, something is Turing complete if it can compute arbitrary programs that can be computed with M bits of memory, where M is arbitrary but finite.
- charcircuit 4y agoSo in common parlance finite state automata are Turing complete? That definition doesn't make any sense.
- 4y ago
- foobiekr 4y agoMost of these are pretty familiar if old enough but this is a wonderful list. I didn’t know C23 was getting rid of trigraphs. That’s probably a good thing and easy to clean up if needed.
- int_19h 4y agoThe bit about "register" is old enough that I don't think it's meaningful anymore. The stock verbiage about how modern compilers ignore "register" because they can do better but it may be useful on simpler ones, has been around in this exact form 20 years ago already. And one curious thing is that even back then, such statements would never list specific compilers where "register" still did something useful. So far as I can tell, "register" was in actual use back when many C compilers were still single-pass, or at least didn't have a full-fledged AST, and thus their ability to do things like escape analysis was limited. With that in mind, "register" was basically a promise to such a compiler to not take the address of a local in the function body (this is the only standard way in which it affects C semantics!). But we haven't had such compilers for a very long time now, even when targeting embedded - the compilers themselves run on full-power hardware, so there's no reason for them to take shortcuts.
- taviso 4y agoIt's still useful on non-simple compilers when mixing inline assembly and c, for example `register __m128 foo asm("ymm7")`. I realize that's a gnu extension - but it's super useful!
- int_19h 4y agoIt definitely is, but it's that ability to bind it to a specific register that makes all the difference - and, of course, that can't be portable C (although it would be nice if we had non-portable supersets per arch, so that e.g. all x86 compilers would do this the same way).
- camel-cdr 4y agoI think register is closer to const, as in: it's a hint to the programmer not the compiler. So if you want to make absolutely sure that a variable can always be in a register then you should consider adding the register specifier to stop other programmers from taking the address of that variable.
- kevin_thibedeau 4y ago> The 0 width field tells that the following bit fields should be set on the next atomic entity (char). This isn't correct since int can't be less than 16-bits. Fields are placed on the nearest natural alignment for the target platform, which might not support unaligned access.
- titzer 4y agoC is fundamentally confused, because it offers (near) machine-level specifications but then leaves just enough wiggle room for compilers to "optimize" (through alignment and such) while ruining the precision of a specification. You end up not getting exactly what you want at the machine level. It's infuriating. The bitfield stuff in C would be fantastic if it weren't fundamentally broken. E.g. some Microsoft compilers in the past interpreted bit fields as signed...always. In V8 we had a work around with templates to avoid bitfields altogether. Fail.
- HybridCurve 4y agoI think the problem with this is the C compiler has to find a solution which work with all the architectures it is expected to support. It order to achieve this, it must generalize in some areas and have flexibility in others. C programmers are required to be familiar with both the specifics of the architectures they are building for and the idiosyncrasies of their complier. I always assumed most other compiled languages were like this since I started with C and moved to x86 assembly from there. However, the more I read about people disliking C for this reason, the more I believe this may not be the case. > The bitfield stuff in C would be fantastic if it weren't fundamentally broken Bitfields in C can be manageable. Each compiler has it's own set of rules for how it prefers to arrange and pack them. Despite them not always being intuitive, I use them regularly since they are so succinct. If you are concerned about faithful and predictable ordering you generally just have a test program which uses known values to verify your layout as part of your build configuration or test battery. > ... some Microsoft compilers ... I've used many C compilers but I have always avoided microsoft ones, going so far to carry a floppy disc with my own when working in the lab at school.
- ikran03 4y agoToo young to know about anything in there, but these look so interesting. Can't wait to show off '%n' in my next uni project
- nstbayless 4y agoHere's another one. Handy "syntax" that makes it possible to iterate an unsigned type from N-1 to 0. (Normally this is tricky.) for (unsigned int i = N; i --> 0;) printf("%d\n", i); This --> construction also works in JavaScript and so on.
- titzer 4y agoAFAICT this would parse as "(i--) > 0", there's no "-->" operator.
- texaslonghorn5 4y agohttps://stackoverflow.com/questions/1642028/what-is-the-operator-in-c https://stackoverflow.com/questions/1642028/what-is-the-oper...
- Jorengarenar 4y agoI was hesitant to put it on the list, but fine, you convinced me
- stonegray 4y agoIf you're gonna test the i--, shouldn't it fall through on zero anyway? for (unsigned int i = N; i--;){} unsigned int i = N; while(i--){ ... } Also I think I'm missing the tricky part. Couldn't this be a bog-standard for loop? for (unsigned int i = N - 1; i > 0; i--){ ... } The "downto" pseudooperator definitely scores some points for coolness and aesthetics, but there's no immediately obvious use case for me.
- Jorengarenar 4y agoThe former executes loop when `i` is 0. And we cannot change the comparison to `>=` in the later, because unsigned is always bigger or equal 0, thus we would get infinite loop.
- skribanto 4y agohow would you iterate over every possible value of a unsigned int?
- localplume 4y agoI remember once upon a time I thought C was fairly simple, so I decided to write a program to generate ASTs from C programs. I was very wrong and it was kind of a nightmare. There are so many weird little quirks or lesser-used features that I never saw in the wild even in large production codebases; I feel like you really don't _need_ a lot of these features. I can't imagine doing proper compiler work, especially for something like C++. Nice article.
- HybridCurve 4y ago> I remember once upon a time I thought C was fairly simple, so I decided to write a program to generate ASTs from C programs. Oh man, I think we all have been this young and naive at some point. I have spent time working with compilers for this purpose (having realized I did not want to attempt parsing source and generating the AST) and decided it is much easier to let them do the work. That being said, it can still be more than a handful (both GCC and Clang have their eccentricities) and depending on how you are using it you still might be in over your head. When you start a project like this and end up failing because you simply do not have the depth of knowledge or time to see it to completion it often feels a bit demoralizing from the loss of investment. Truthfully though, having started many such ventures (emulators for 6502 and 80386 to name a few), you get all the benefit of experience from working on a difficult problems without the misery of debugging and model checking until everything until is more/less perfect. It's great fun, you learn a lot, and you should never avoid trying simply because it might be too much to handle.
- deleted 4y ago[deleted]
- 6451937099 4y ago[dead]
- Joker_vD 4y agoNever quite understood why compound literals are lvalues, but fine, whatever, I guess, it's so that you can write "&(struct Foo){};" instead of "struct Foo tmp; &tmp;"... which, on a tangential note, reminds me about Go: the proposals to make things like &5 and &true legal in Go were rejected because "the implied semantics would be unclear" even though &structFoo{} is legal and apparently has obvious semantics.
- leni536 4y agoIn C compound literals have a relatively long lifetime compared to C++ temporaries. With these lifetime rules it makes sense that they are lvalues, although I like C++ rvalues (especially prvalues) more. https://cigix.me/c17#6.5.2.5.p5 https://cigix.me/c17#6.5.2.5.p5 > If the compound literal occurs outside the body of a function, the object has static storage duration; otherwise, it has automatic storage duration associated with the enclosing block.
- cataphract 4y agoIt's useful when a function has a out or in/out struct parameter whose value at the end you're not interested in. Or in functions where the struct is an input parameter, but they return it as a return value too, which you can then assign to a pointer variable or immediately pass to another function. Note that the struct values thus created have longer lifetimes than temporary C++ objects created directly inside the argument list of a function call.
- flohofwoe 4y agoThat's extemely useful for invoking a function that takes a pointer to a struct, but you don't want 'taint' the code with a temporary variable that's only needed for the function call: func(&(bla_t){ .x = 0, .y = 1 });
- gallier2 4y agoCompound literals are anonymous variables, i.e. like variables except that there is no label visible in the C program. If you look at the assembly you'll see that they are declared exactly like a variable except with a compiler generated internal label.
- 6451937099 4y ago[dead]
- milgra 4y agoVery nice collection. My favorite C feature is actually a gcc/clang feature : the __INCLUDE_LEVEL__ predefined macro. It made me code&maintain my C projects exactly twice as fast as before because file count dropped to half : https://github.com/milgra/headerlessc https://github.com/milgra/headerlessc .
- 3836293648 4y agoHow do you handle third party headers?
- milgra 4y agoI just include them, everything works as before. If I have to create a library then I create a separate header file for the api functions and only the internals are headerless.
- gavinray 4y agoThis is great!
- enriquto 4y agoI love this. Somehow it feels more elegant than "header-only" libraries.
- xigoi 4y agoHow does this help? It just moves the content of the .h file to the .c file, but you still need to Write Everything Twice.
- milgra 4y agoIt reduces file count in the project browser, renaming/refactoring is much simpler in one file, it just feels like working with a newer language.
- account42 4y agoIs having two files really that much of a bother? I have my editor set switch between the .c(pp) and the .h with a keyboard shortcut and that seems easier than scrolling between declaration and definition when you want to change something.
- zwieback 4y agoBe interesting to see when these features showed up. I learned C from the K&R book back in the day and it doesn't mention most of these. Designated initializer is something I'll try to remember, seems handy.
- suprjami 4y agoDesignated initialisers were added in C99
- AceJohnny2 4y agoYeah the K&R, while being a masterpiece of clarity and conciseness, is severely outdated in many important ways. I wish there was some effort to create a modern version while preserving the clarity and conciseness of Kernighan and Ritchie. Designated initializers in particular are extremely useful. I once halted a factory line for days because of a mistake they would have avoided.
- flohofwoe 4y agoDesignated init and compound literals were added in C99. I think there are two reasons for those features not being better known: 1) C++ 'forked' their C subset before C99 (ca. "C95"), and while C++20 finally got its own version of designated init, this has so many restrictions compared to C99 that it is basically pointless. 2) MSVC hasn't supported any important C99 features until around 2016
- neverrroot 4y agoIt’s cool to have these, it’s fun to use them for fun. But please don’t use them in production code. Also don’t assume most of them will he known by other developers.
- suprjami 4y agoProfessional C developers definitely should be using at least designated init and FAM, standard features both added in C99 and currently 24 years old.
- neverrroot 4y agoWell, it’s either a lesser known trick or it’s something people should be using. In general using lesser known tricks it’s not a good idea for production code. But I understand there are cases where there is no good alternative, so it’s warranted.
- suprjami 4y agoMy point is that these aren't "lesser known tricks". They're important language features which solve real problems and which anyone writing production C should be at least aware of, if not actively using for the advantages they provide.
- neverrroot 4y agoI was taking for granted the storyline, and assumed they are indeed lesser known and tricks. To me it’s never about what I know, it’s about what the others know, my production code is not mine alone, and I also don’t want to be responsible for it forever. So I try to be explicit and use no tricks wherever possible.
- suprjami 4y agoI agree with this in principle, but where do you draw the line between language features (good) and obscure tricks (bad)? Is your team really writing only ANSI C89 from the K&R 2nd edition book and ignoring the last ~35 years of language improvements?
- LegionMammal978 4y ago> volatile type qualifier > This qualifier tells the compiler that a variable may be accessed by other means than the current code (e.g. by code run in another thread or it's MMIO device), thus to not optimize away reads and writes to this resource. It's dangerous to mention cross-thread data access as a use case for volatile. In standard C, modifying any non-atomic value on one thread, while accessing it on another thread without synchronization, is always UB. Volatile variables do not get any exemption from this rule. In practice, the symptoms of such a data race include the modification not being visible on the other thread, or the modified value getting torn between its old and new states.
- deleted 4y ago[deleted]
- Jorengarenar 4y ago`volatile` is one of the things we need to pay attention to when dealing with threads, but as you notice it's not the only one. Eskil Steenberg talks about it at 12:42 in his talk Advanced C: The UB and optimizations that trick good programmers. [0] [0]: https://youtu.be/w3_e9vZj7D8?t=762 https://youtu.be/w3_e9vZj7D8?t=762
- plugin-baby 4y agoUB?
- G3rn0ti 4y ago„undefined behavior“
- deleted 4y ago[deleted]
- mid-kid 4y agoThese days it can be chained with _Atomic to achieve the desired effect. That said, oftentimes you need more serious synchronization mechanisms your library would provide.
- clnq 4y agoDo we have something like this for C++ (parts not shared with C)?
- HybridCurve 4y agoCan't say for all, but I am reasonably certain C++ does not support designated initializers/sparse array definitions. Some of these features where added in more recent revisions to the C specification from which C++ has diverged from. I would expect most of the differences would become more pronounced starting with C99.
- flohofwoe 4y agoIn theory, C++20 has designated init, but it has so many restrictions compared to C99 that it is pretty much useless for real world code.
- int_19h 4y agoOne non-obvious thing about named function types is that they can also be used to declare (but not define) functions: typedef void func(int); func f; void f(int) {} I don't think I've ever seen a practical use for this in C, though. In C++, where this also works, and extends to member functions, this can be very occasionally useful in conjunction with decltype to assert that a function has signature identical to some other function - e.g. when you're intercepting and detouring some shared library calls: int foo(); decltype(foo) bar; I suppose with typeof() in C23 this might also become more interesting.
- mtklein 4y agoI have found this pretty handy for declaring a bunch of functions of all the same type, e.g. steps in a direct-threaded interpreter. typedef void Step(whatever...); Step add,sub,mul,div, load,store, etc...;
- torstenvl 4y agoI've seen this used to define function pointers / callbacks in a "cleaner" way. I think Mg does this IIRC.
- mtklein 4y agoOh yes! I totally agree with that approach; I find it much clearer to typedef the function type rather than the function pointer type: typedef int (*CallbackPtr)(int,int); void foo(..., CallbackPtr callback); always reads less clearly to me than typedef int Callback(int,int); void foo(..., Callback* callback); I find this especially useful in C++ where the callback type could conceivably be some other type like std::function. Seeing that * helps me know at a glance it's probably just a plain old function pointer. Though I think maybe clearest of all is to not use a typedef, provided it doesn't cause other readability problems: void foo(..., int (*callback)(int,int)); (Not meaning to steal your thunder here... just wanted to write out an example in case anyone else was curious.)
- int_19h 4y ago
- AceJohnny2 4y agoCompound Literals in C are great. They're no surprise to anyone coming from more sophisticated languages, but I've never seen them used in the C codebases I've worked on. What with C also allowing structures as return values, another rarely-used feature, they're really useful for allowing a richer API than the historical `int foo(...)` that so many people are used to seeing. C has so much legacy that it's really hard for even decades-old (C99!) feature to impose themselves. Or perhaps that's MSVC's lagging support that's to blame :p
- deleted 4y ago[deleted]
- pjmlp 4y agoMSVC supports C11 and C17, minus the C99 stuff that was made optional in C11. Anyway given the option, one should always favour C++ over C, if they care about secure code, which while not perfect it is much better than any C compiler will do.
- AceJohnny2 4y ago> Anyway given the option, one should always favour C++ over C Eh. I work in embedded, where C reigns supreme. C++ has its own issues in the area, namely that you need to construct your own sub-dialect that removes some features of C++ to make it fit embedded constraints. Commonly, it's C++-but-no-exceptions, sometimes C++-but-no-templates, and others. That said, I'll grant that us embedded developers are effectively "traumatized" and have difficulty accepting new approaches because we're too focused on certain paradigms that are no longer relevant (See my previous about returning structures, which has encountered responses like "but then it might do an extra memcpy()!!11")
- account42 4y ago> sometimes C++-but-no-templates I can understand exceptions, but what constraints require you to ban templates? If its just code size then it seems a bit arbitrary to ban them completely. AFAIK most users of C++ do ban some features in their projects so I don't see why that specifically is holding embedded back. Disabling exceptions specifically is something that is not unheard of outside embedded either.
- tastysandwich 4y ago"Expert C Programming: Deep C Secrets" is a really good book to learn a lot of C tricks and quirks, plus some history. I read it a few years ago and loved it. I was a grad when I read it and remember annoying my older coworkers for a few weeks with little gotchas I picked up. "hey what do you think THIS example prints?" "Stop sending me these!"
- deleted 4y ago[deleted]
- jjgreen 4y agoFabulous book, includes a section "Bus Error, Take the Train" explaining the "bus errors" one found on Sun hardware ...
- defrost 4y agoWhen I wrote a lot of cross platform performant numerics I loved the bus error thrown by the Sun machines. In essence it was a RISC machine saying "I can't deal with unaligned data" and a sign that code was (say) storing a 32 or 64 bit int that straddled a word boundary and the hardware was not coping on a MOV. Why did that matter and why was it good thing? It forced programmers to think about data alignment and resulted in code that run faster on CISC Intel chips. The dirty secret about Intel chips was they "just did it" w/out complaint - and it slowed them down significantly on pipelined computations if they were constantly double handling unaligned data to get it from memory (across a word boundary) to bus (aligned for transit) to memry again (across a word boundary).
- somewhereoutth 4y agoI'm a bit better at English than c, and in the spirit of language peculiarities, this jumped out at me: > It's possible, because C cares less than more about whitespace Idiomatically we'd say 'couldn't care less'. I guess we should be glad it wasn't the diabolical and illogical 'could care less'
- Jedd 4y agoI don't believe those are functionally / semantically equivalent - couldn't care less does imply a min() value of care. In contrast, the author is suggesting a comparative only. And, on careful re-reading, I suspect the author is having a play on syntax & semantics here -- the context of the quote is: > You may ask, since when C has such operator and the answer is: since never. --> is not an operator, but two separate operators -- and > written in a way they look like one. It's possible, because C cares less than more about whitespace. Given that '--' is decrement (kind of 'lessen') and > is greater than (kind of 'more'). Perhaps I am reading too much into that. (I feel 'couldn't care less' is perhaps more common in northern America than elsewhere, and while TFA has a Gabon TLD, appears to be resident in Poland, so automatically receives a lot of leeway in their use of idiomatic English.)
- Jorengarenar 4y agoHah, unfortunately this bit of "poetry" wasn't intentional. I just meant that C does care about whitespace, just not a lot.
- somewhereoutth 4y agoAh apologies - I missed that subtlety. Unfortunately the whole 'could care less' debacle has left me somewhat triggerable. 'cares not so much' / 'doesn't care so much' might also work in your context.
- zabzonk 4y agoc does care about whitespace - compare: int x; with: intx;
- teo_zero 4y agoI think the 5[arr] deserves more love. // traditional syntax: boxes[products[myorder.product].box].weight // index[array]: myorder.product[products].box[boxes].weight
- Jorengarenar 4y agoDon't mind if I do
- panzi 4y agoBefore reading it, does it include the array[index] == index[array] thing again? reading Yep it does. :D
- Razengan 4y agoI wish there was a language "between" assembly and C: basically assembly with some quality-of-life improvements. Shortcuts to reduce redundant chores (like those multiple instructions to load one 64-bit number into an ARM register) but minimal "magic" or unintended consequences as in C. Things like maybe a function call syntax like: CALL someFunc(R1: thingForRegister1, @R7: pushR7ThenPopOnReturn, R42: [memoryAddressForR42]) and the function might be defined as: someFunc(R1 as localNameForR1, R7 as oneMoreThing, R42? as optionalArgument) and so on. (but anyone could come up with better ideas than me)
- xigoi 4y agoI think QBE might be what you're looking for? https://c9x.me/compile/ https://c9x.me/compile/
- spc476 4y agoYou just need a decent assembler with macro support. Here's a few lines of code I have for 32-bit x86 code: PRINTF "main_task: sf=%p",ebp PRINTF " sf: 1=%d 2=%d 3=%d 4=%d",dword [ebp+8],dword [ebp+12],eax,ebx I'm using NASM and it wasn't hard to write the macro to do so.
- joshthecynic 4y ago[dead]
- jokoon 4y agoI'm currently trying to make a programming language that translates to C. I've started with a pseudo BNF parser. I started reading the C BNF and I have to admit that I was not prepared at all. It's not as easy as it sounds. I cannot imagine how difficult it must be to maintain a modern C++ compiler.
- lionkor 4y agoYeah, good luck hahaha There is a reason why modern languages use keywords like "func", "def", "fn", "var", "let" to discern between different types of declarations, for example. I dont think many languages are LL(k) (please correct me if im wrong), but C is as far away from that as it gets, for small k.
- Gibbon1 4y agoI've thought it would be nice to add func to the language. Even if it's just sugar it would help. Type inference + allowing functions to return an anonymous struct or a tuple would be super.
- augustk 4y agoThere are three macros which I find indispensable and which I use in all my C projects, namely LEN, NEW and NEW_ARRAY. I keep them in a file named Util.h: #ifndef UTIL_H #define UTIL_H #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define LEN(arr) (sizeof (arr) / sizeof (arr)[0]) #define NEW_ARRAY(ptr, n) \ (ptr) = malloc((n) * sizeof (ptr)[0]); \ if ((ptr) == NULL) { \ fprintf(stderr, "Memory allocation failed: %s\n", strerror(errno)); \ exit(EXIT_FAILURE); \ } #define NEW(ptr) NEW_ARRAY(ptr, 1) #endif With these in place, working with arrays and dynamic memory is safer, less verbose and readability is improved.
- GnarfGnarf 4y agoI found a lot of bugs went away when I switched to STL (Standard Template Library) arrays and ditched managing my own memory. That's C++, I guess it's not available in straight C?
- krapp 4y ago>That's C++, I guess it's not available in straight C? No, because C doesn't have templates. The best you can do for a "vector" in C is macros like above, that also realloc, or write an API around structs for each type.
- GnarfGnarf 4y agoToo bad. STL deque's are non-contiguous, allow for much bigger arrays. I had an application that used vector<>, ran out of contiguous memory. deque<> solved the problem.
- augustk 4y agoIt's even easier if you use a garbage collector, like for instance libgc. Then you just replace (ptr) = malloc((n) * sizeof (ptr)[0]); with (ptr) = GC_MALLOC((n) * sizeof (ptr)[0]); in the macro and don't have to worry about calling free.
- Gibbon1 4y ago
- mnaydin 4y agoI am not able to understand how int (*ap3)[900000] = malloc(sizeof *ap3); is nicer than int *a = malloc(900000 * sizeof *a); Notice that, in the former case, the array elements must be accessed as (*ap3)[i] whereas in the latter case the usual method a[i] is fine.
- syockit 4y agoMe neither. In the VLA use cases page he does that with multidimensional array too. int (*arr)[n][m] = malloc(sizeof *arr); which you have to access with (*arr)[i][j] I prefer doing int (*arr)[m] = malloc(n*sizeof(*arr)); though this separates m and n to be one on left side while the other on the right side, it allows me to index directly arr[i][j]
- teo_zero 4y agoYou don't use this technique to define a new array, but to get a pointer to an array you already have. The goal is to have a pointer to the array, and not a pointer to the first element of the array. Whether this is "nicer" or not, and whether this is what you need in your application, are out of the scope of the fine article.
- michaelcampbell 4y agoWhen I coded C my go-to for fun stuff like this was Peter Van Der Linden's "Deep C Secrets" book. One of those I don't need, but wish I hadn't sold.
- ljosifov 4y agoGreat read, and lead me to "When VLA in C doesn't smell of rotten eggs" https://blog.joren.ga/vla-usecases https://blog.joren.ga/vla-usecases and this: int n = 3, m = 4; int (*matrix_NxM)[n][m] = malloc(sizeof *matrix_NxM); // `n` and `m` are variables with dimensions known at runtime, not compile time if (matrix_NxM) { // (*matrix_NxM)[i][j] = ...; free(matrix_NxM); } Well, that makes much easier a few things I'm doing atm, really glad I read it.