This post was originally written on Codeforces; relevant discussion can be found here.
This post was initially meant to request updates to the C compiler on Codeforces (given the large number of posts complaining about “mysterious” issues in their submissions in C). While writing it, I realized that the chances of the said updates would be higher if I mentioned a few reasons why people would like to code in C instead of C++ (some of the reasons are not completely serious, as should be evident from the context).
Before people come after me in the comments saying that you should use Rust for a few reasons I will be listing below, please keep in mind that I agree that Rust is a good language with reasonable design choices, and I prefer C over Rust, at least for now.
Commonly encountered issues in C on Codeforces
- The C compiler on Codeforces is currently 32-bit, and hence it suffers from the same disadvantages as C++ faces compared to the 64-bit version. For instance, 64-bit arithmetic is slower than it could be (and leads to a ~2x slowdown in many cases).
- There is an issue with slow IO using stdio, as I mentioned in
this
comment. Paraphrasing the issue here,
this post mentioned a
bug that was making the earlier MinGW implementation of IO very slow,
and it was patched to fix this issue. This is also why the newer C++
compilers on Codeforces have a decently fast implementation of
scanf=/=printf
compared to the older benchmarks in the post. However, this is not the case for C. My hypothesis is that this issue wasn’t patched for the C compiler. There is, however, a quick fix for this issue: add this line to the top of your submission, and it will revert to the faster implementation (at least as of now):#define __USE_MINGW_ANSI_STDIO 0
. Thanks to ffao for telling me about this hack. - It seems that there is an issue with the ancient GCC version used for
C11. For instance, when using the
timespec
struct to get high precision time, there is a compilation error (and it seems it is because of limited C11 support in GCC 5.1). Update: a similar issue arises (timespec_get
not found) when I submit with C++20 (64) as well, so I think this is an issue with the C library rather than the compiler.
A possible fix to the above issues that works most of the time is to submit your code in C++20 (64 bit) instead of C11. You might face problems due to different behavior of specific keywords or ODR violations due to naming issues, but it should be obvious how to fix those errors manually.
Some update requests
Coming to the original point of the post, I have a couple of update requests (if any other updates can make C easier to use, feel free to discuss them in the comments). Tagging MikeMirzayanov so these updates can be considered in the immediate future.
- Adding C17 (64 bit) alongside the old C11 option: This should be doable using msys2 as done for C++. I am somewhat confident that not using MinGW for this purpose would make the slow IO issue disappear. Also, the reason why I recommend C17 instead of C11 is that C17 is the latest version that addresses defects in C11. Note that C17 and C18 are two names for the same thing. However, I still believe that the current C11 option should be available in the future as well, for the sake of completeness/more options/other reasons mentioned in the comments below.
- Fixing slow IO: If the above doesn’t fix the slow IO issue, perhaps there could be a way to fix it in MinGW itself. I am not too familiar with this, though.
- Fixing the
timespec_get
issue (and other C11/C17 support issues): I am not exactly sure on how to go about fixing this, but since this seems to be a C library issue, it could be a good idea to try installing a C library that has this functionality (and is C11 compliant). The issue probably boils down to Windows not having libraries that support everything in the C standard, since C11/C17 works perfectly on my Linux system.
Now I’ll discuss why C is relevant for competitive programming.
Why use C?
Why C++ is preferred over C
Of course, while choosing programming languages for competitive programming, C++ is usually recommended to beginners (over C and other languages) because of reasons that include the following:
- It is fast: writing assembly manually almost always leads to a slower program than writing an equivalent program and letting the compiler generate the program. However, with C, this is a non-issue most of the time since common compilers support both C and C++ and often use the same optimizations.
- STL: there is (in my opinion) a reasonably well-designed standard library that contains handy data structures and algorithms with a fairly generic API. C has a much more limited library but is minimal in the true sense with a non-prohibitive overhead.
- Not just imperative/object-oriented: Using lambdas can make code much
cleaner and life much easier. There is support for function pointers
in the C standard library (for example, you can use comparators in
qsort
and so on, with some overhead, though it is generally better to write your own sort to avoid the overhead of using function pointers completely).
Some of its less-noticed advantages over C in the context of competitive programming (apart from the obvious benefits of having a good set of data structures and algorithms) are:
- Good random number generation: If you are writing a randomized
solution, it is imperative that you randomize the seed using something
that can’t be predicted easily. Seeding
rand
withtime(NULL)
is pretty bad, so in C, the way to do this is very platform specific until C11 (and people are not aware of how significant a revision C11 was, compared to C99 and other revisions before it). Also,rand
is terrible in general, so using a C port of a C++ STL random number generator is probably a better bet. For reference, this is a good random number generator. constexpr
(and template) support: If you enjoy constant-factor optimization, you would probably miss this. In certain fast IO implementations, for instance, a constexpr array is built at compile time to use chunks of bytes at a time rather than a single byte. Templates are important because writing your own library comes in handy in contests. However, using_Generic
, it is possible to write generic code to a certain extent as well.
Why C is actually not that bad
Despite these disadvantages, it is possible (and might also be helpful in training) to use C for contests. C11 (which should be abandoned in favor of C17 due to C17 having fixes to C11 defect reports) has a standard library that is seldom talked about and has some of the following features which make life simpler:
tgmath.h
: Type generic math for floating point numbers and complex numbersstdint.h
andinttypes.h
: Types likeint64_t
are also available in C, contrary to popular belief.- As mentioned earlier, there is some support for generics and function pointers in the standard library.
- There is support for booleans (rather than having to use integer types for everything) as well. C23 changes that a bit, but that is not going to be an issue unless you’re doing fairly advanced things.
If you are using GCC and glibc, there are more surprises in store for you that make C all the more usable:
- Most pragmas and builtins carry over from C++ to C, making optimizing things almost as easy as in C++.
- Contrary to popular belief, the glibc implementation uses mergesort
for
qsort
if sufficient space is available (which is pretty much always the case in competitive programming), so no worrying about quicksort being hackable!
Why you would want to code in C
Now that I have explained why C isn’t as bad to code in as you might have thought earlier, you might wonder what the point of using C is at all. After all, isn’t C++ meant to be a more supposedly “user-friendly” language than C? (In fact, it is not, with all its language complexities – for instance, I haven’t heard of a popular language that has as many “value-categories” as C++ and is as easy to shoot yourself in the foot with, with the sole exception of JS – but that is a topic for another day).
Straight to the point, competitive programming in C has the following benefits:
- It forces you to devise a more straightforward implementation and
think the problem through in more detail, improving your
problem-solving (at least marginally) and implementation skills. If
you find yourself using horrible stuff like
map<int, pair<set<int, greater<int>>, FenwickTree<double>>>
for a problem whose editorial uses a single Fenwick tree or sorting + two pointers, you might want to consider using a language which is not this powerful (of course, using these complicated data structures can also lead to unnecessary TLEs and overcomplicate your implementation). C is the best example; in terms of algorithms bundled in the standard library, there are only two algorithms: sorting and binary search, which are well-known to be the only “hard-to-write-for-beginners” algorithms that you would need before you hit GM. This is, of course, a bit exaggerated, but if you want to hit GM, you would probably just want to get better at implementing easier problems first, after which it is only natural to try to write your own library. Writing a treap/hash table for simple data types is probably all you would ever need before that. - It is much easier to “completely” learn C than to learn C++ (or most other popular modern languages), in the sense that you can go through the C standard in a couple of sittings and apply it as well. The cppreference page for C (yes, cppreference also has a C reference) is a good resource for learning C if you already know a bit of C and/or C++, and much less daunting compared to that for C++.
- It is a great way to kill time and/or challenge yourself, as well as learn about how stuff works under the hood; more modern languages tend to get lost under too many layers of abstraction, making it hard to reason about which parts of your program aren’t working as you expected and which parts could be much faster if written differently.
- Depending on how you look at it, you could also use it to gain a false sense of superiority over people who abuse language features to get AC in the same time that you did.
Coding in C is probably not a big deal for you at all if you are rainboy, anyway.