Announcement

Collapse
No announcement yet.

Linux Kernel Moving Ahead With Going From C89 To C11 Code

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #31
    Originally posted by coder View Post
    intermingled declarations and code: variable declaration is no longer restricted to file scope or the start of a compound statement (block)
    This is the big one. It's hard to go back to ANSI C when you get used to this.

    Comment


    • #32
      Originally posted by sinepgib View Post

      I don't know which toolchain you used, but GCC and clang pretty much always have full support on day 1. It's MSVC that didn't even support C99 in 2011 at least (when I learned programming), and if I have to guess doesn't even support it today. But that speaks of MS support for C, rather than the common picture.
      It's basically possible to compile C11 code that only uses C89 features, and probably has been for a long time. But I was looking to, in a cross-platform code base, to replace pthreads with the C11 threading features that should in theory be more cross-platform, and I'm not sure there is any mainstream platform that fully supports it yet. This is about 10 years after the introduction of C11.

      Not that this is relevant to the kernel at all, since the kernel does not use any libc functions. But for all of the things I saw that did not work, I have to assume there is a lot more that I didn't find.

      Comment


      • #33
        Originally posted by betty567 View Post
        I'm not sure there is any mainstream platform that fully supports it yet. This is about 10 years after the introduction of C11.
        As I mentioned above, threading is an optional part of C11. So, you could have C11-conforming implementations which don't offer it.

        If you're curious about the nuances of what GCC offers, there's this:



        However, threading is part of the standard library. GNU's libc does claim to offer it:

        Comment


        • #34
          (C99 differences from C90 - intermingled declarations and code: variable declaration is no longer restricted to file scope or the start of a compound statement (block))

          Originally posted by bison View Post

          This is the big one. It's hard to go back to ANSI C when you get used to this.
          In C90 programming, it is common to have all your variables declared at the top of a function without initialisation, then assigned and used in the body of the code. But you don't have to do it that way - variables can be declared at the start of blocks within the code, and you can initialise variables with expressions. C99 gives a lot more flexibility, and it allows a style of declaring variables only when you have something to put in them - something many people find gives clearer code and allows better static error checking (especially when you use "const" liberally). Some people prefer to separate variable declaration and use, and think non-trivial initialisations and mixing code and data declarations jumbles code and encourages larger functions. (I personally much prefer the "declare when you are ready to initialise" style.)

          The kernel programming style is an ugly mixture of the two - variables are mostly declared at the top of functions but sometimes at the start of other blocks. They are sometimes initialised, sometimes not. It combines the worst of both styles without any of the benefits. Hopefully allowing C99 and newer style will improve on that.

          (Technical nitpick - officially, "ANSI C" tracks the current version of "ISO C", and thus means "C18" at the moment. People typically use the term to mean "C89", but you might find yourself in conversation with a pedant who likes pointless quibbles. "C90" is the same language as "C89", but it is in the ISO format instead of the ANSI format and has different section numbering. C18 is the current standard, as it was finalised in 2018, but it is often called C17 because that's when most of the work was done.)

          Comment


          • #35
            Originally posted by coder View Post
            Good news! However, it's going to make back-porting of patches annoying, but it's a pill that eventually has to be swallowed for the kernel to keep moving forward.

            I wish C would adopt templates. Even as I say that, it seems as if C++ is trying to move away from them, as much as possible. I guess C11 tried to split the difference, with the _Generic keyword.
            C cannot implement templates or anything directly equivalent without introducing name mangling - and that would be a huge change to the language and its tools. It could only have a template-style feature at the pre-processor level, since macro names are not exposed to linking - and that is exactly what you get with _Generic. The prime motivation for _Generic is to allow the C99 type-generic maths functions in "tgmath.h" to be implementable in a portable manner, and it works fine for that.

            I don't know where you got the impression that C++ is "trying to move away from templates". Generic programming in C++ is getting more and more common, with a move towards template-based header-only libraries. A big new feature in C++ is "concepts", which allow you to write templated code in a simpler and neater fashion that better expresses the requirements for your types and allows tools to give clearer error messages, but it's still all templates.

            Comment


            • #36
              (Regarding VLAs)

              Originally posted by Steffo View Post

              I don't think so. How do you handle the error, when the memory is not sufficient? You can easily get a stack overflow.
              You can just as easily get a stack overflow with local variables that are arrays of constant size, which are perfectly legal in C89. And "alloca" has a similar purpose to VLAs, and has been standard in POSIX for decades. (I don't know if there are any kernel coding guidelines about these things.) You avoid stack overflow in your VLA's by making sure you don't make them bigger than appropriate - it's no different from what you should be doing before allocating dynamic arrays on the heap. Basically, if you think VLA's are a risk then you should be thinking long and hard about whether C is the right language for you, or if you really have understood the situation.

              Comment


              • #37
                Originally posted by DavidBrown View Post
                you don't have to do it that way - variables can be declared at the start of blocks within the code, and you can initialise variables with expressions.
                Correct me if I'm wrong about this, but I seem to recall there were restrictions on what you could initialize them with. For instance, you couldn't initialize them with the return value of a function (except for maybe the last variable in the block?).

                Originally posted by DavidBrown View Post
                C99 gives a lot more flexibility, and it allows a style of declaring variables only when you have something to put in them - something many people find gives clearer code
                I find it reduces the temptation to reuse a variable in some way that changes its meaning. And when your variables have only one meaning, then you can focus more on their semantics and ensuring they're more clearly reflected in their names.

                There are also practical benefits. Having them defined closer to the point of use means less looking or scrolling up and down, to see the variable's type or with what it was initialized. Having variables more narrowly-scoped also means fewer things to keep in your head, as you read the code. Finally, defining them closer to the point of use reduces risk of unintentional shadowing of the same-named variable in a larger scope (i.e. assuming the larger scope is reduced, as well).

                Originally posted by DavidBrown View Post
                Some people prefer to separate variable declaration and use, and think non-trivial initialisations and mixing code and data declarations jumbles code and encourages larger functions.
                Old habits die hard. It's a good exercise occasionally to re-evaluate your assumptions and the tradeoffs implicit in your coding style.

                And sometimes, a shift in one aspect of a coding style should be accompanied by other changes. For example, I find that I have more difficulty reading code that uses C++'s auto keyword, unless the variable names encompass some of the information that was formerly supplied by its type name. So, if you love auto, maybe you should consider following variable naming conventions like those used in dynamically-typed languages (e.g. Python).

                Comment


                • #38
                  Originally posted by DavidBrown View Post
                  (Regarding VLAs)



                  You can just as easily get a stack overflow with local variables that are arrays of constant size, which are perfectly legal in C89. And "alloca" has a similar purpose to VLAs, and has been standard in POSIX for decades. (I don't know if there are any kernel coding guidelines about these things.) You avoid stack overflow in your VLA's by making sure you don't make them bigger than appropriate - it's no different from what you should be doing before allocating dynamic arrays on the heap. Basically, if you think VLA's are a risk then you should be thinking long and hard about whether C is the right language for you, or if you really have understood the situation.
                  First: The heap is much (!) bigger than the stack. Second, you don't get heap allocation if the memory is not sufficient. You get a null pointer instead which you check against.
                  Third: How do you handle the situation when you need more memory than the stack offers? This is highly insecure and leads to not easy readable code.
                  I'm pretty sure VLA is not allowed in kernel space.
                  And no: I use C++ and Rust where such an optimization is used securely by appropriate libraries.

                  Comment


                  • #39
                    Originally posted by DavidBrown View Post
                    I don't know where you got the impression that C++ is "trying to move away from templates".
                    They keep introducing new language features that seem to chip away at the use cases for templates. For instance, C++17 now has constexpr if statements. A co-worker once complained to me that he wanted to use one, but couldn't due to our build still using C++14. I wrote him a good 'ol C++03-compatible template solution that accomplished the same goal.

                    Functions with auto parameters are another example that comes to mind, at the moment. Technically, the are templates, but it's a way to write a function template without using traditional template syntax. I'm sure I've run across others.

                    Comment


                    • #40
                      Originally posted by Steffo View Post
                      First: The heap is much (!) bigger than the stack.
                      Absolute size is irrelevant. If you try to allocate heap memory without knowing that the size is within reasonable limits, your program is bad. If you try to allocate stack memory without knowing the size is within reasonable limits, your program is bad. What constitutes "reasonable limits" will differ, but not the principle.

                      Second, you don't get heap allocation if the memory is not sufficient. You get a null pointer instead which you check against.
                      On many systems, the OS will do its best to honour your request. That can be over-allocating, pushing other memory into swap, dropping file caches, killing other programs - whatever it takes. It's not hard to reduce a user to tears through heap allocations that return non-zero but are nonetheless completely inappropriate.

                      Some people like to write "try it and see" code. They may even have code to check if malloc() returns 0. Nine times out of ten, at least, they will fail to handle the error situation correctly and 99 times out of 100 they will not test their error handling code properly.

                      If you write "look before you leap" code, you do it the same way whether you are using VLAs (or alloca()) or the heap.

                      Third: How do you handle the situation when you need more memory than the stack offers?
                      If you are going to be handling sizes that are too big for the stack, you don't use the stack. Isn't that obvious?

                      This is highly insecure and leads to not easy readable code.
                      I'm pretty sure VLA is not allowed in kernel space.
                      And no: I use C++ and Rust where such an optimization is used securely by appropriate libraries.
                      C++ does not support VLAs. (It does support a wider definition of "constant", thus some local arrays whose sizes are known at compile time but are not "constant" by the C definition are normal arrays in C++, and VLAs in C.)

                      VLA's are a tool. You use them when they are appropriate, and when you know how to use them correctly and safely - just like any other feature in any programming language. Used correctly, they are vastly more efficient than heap-allocated arrays for small short-lived arrays. Don't be scared of them - learn how to use them, and use them if they are right for the code in question.

                      Comment

                      Working...
                      X