nor's blog

By nor, 20 months ago, In English

This blog was initially meant to request updates to the C compiler on Codeforces (given the large number of blogs 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 great language with reasonably good design choices (most of them better than C++, at least), and using C is a personal choice.

Commonly encountered issues in C on Codeforces

  1. 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).
  2. There is an issue with slow IO using stdio, as I mentioned in this comment. Paraphrasing the issue here, this blog 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 blog. 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.
  3. 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 blog, 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 with time(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 numbers
  • stdint.h and inttypes.h: Types like int64_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.

  • Vote: I like it
  • +136
  • Vote: I do not like it

| Write comment?
»
20 months ago, # |
  Vote: I like it -26 Vote: I do not like it

Bullshit.

  • »
    »
    20 months ago, # ^ |
      Vote: I like it +42 Vote: I do not like it

    Least you can do is act like an adult and explain what's wrong.

»
20 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Great blog.

»
20 months ago, # |
  Vote: I like it 0 Vote: I do not like it

The moment I read the title of the blog, I was anticipating rainboy pun.

»
20 months ago, # |
  Vote: I like it +32 Vote: I do not like it

While I really like C, and I have been using along with (or it instead of) C++, I think it must be said that C basically worse than C++ for competitive programming. People who use it instead of C++ are just doing it for the extra challenges.

That being said, wouldn't a worse version of C (C11) provide even more challenge, compared to a version without defects (C17)? There are solution for the defects in C11, so it's not like those are impossible to live with.

Personally I support the addition of C17 or newer C in the future, but I think C11 should not be lost for that to happen ("abandoned in favor of C17"). Once upon a time (pre 2017), C++98 and C++11 was available here, but they are now gone. From C++11 to C++14 there might not be much of a change, but from C++98 to C++11 is a massive different, some C++98 code wouldn't compile under C++11 for example. My reasons for preserving old versions of languages in general are:

  • It's better to have more options

  • Bureaucracy in onsite contests often means that newer versions of the language might not be available, personally, I have participated in onsite contests (in 2016-2017 or so) that only support C++98. Some contests also doesn't say anything about the version of the language (no feedback until the contest is over), so the best option is to write code that will compile and work in all the standards. Tldr: Some time people are forced to use old version of the languages, and it's good to have those version available for practicing.

  • »
    »
    20 months ago, # ^ |
    Rev. 3   Vote: I like it +26 Vote: I do not like it

    Thanks for the detailed comment.

    Regarding your first point, the main reason why I say that C is relevant is basically getting some sort of a handicap while coding, in the hope that it will force you to think more towards the solution rather than starting implementing it right away -- I find it very easy to do in C++ but not in C. Of course, in actual competitions (and training to make implementation faster), it is strictly better to use C++.

    The C11 defect reports I mentioned are mostly concerned with some rare weird behavior (and weird edge/ambiguous cases that needed to be ironed out), and are almost irrelevant for competitive programming, so it should be fine to have C11 (64 bit) in most cases. The reason I suggested using C17 for the new option on CF was that C17 has a more sane set of defaults in those edge cases (and fixing defect reports is in fact the only change that happens; no new features whatsoever). C is a much more stable language than C++, and the changes from C11 to C17 are nowhere near as significant as those from any revision of C++ to another.

    I agree with you on not removing C11 (but only adding C17 (64 bit) as a new option) -- I should have phrased that in a better manner.

»
20 months ago, # |
  Vote: I like it +25 Vote: I do not like it

Joke on you I'll just compete in Pascal.

»
18 months ago, # |
  Vote: I like it +45 Vote: I do not like it

Reading your "Why you would want to code in C" completely convinced me. That is, convinced me that I made the correct choice going for C++