1. 32
  1. 17

    So, we first declare a struct, memset it to zero and then initialize the fields we are interested in. This is all fine except for the fact that the same result can be achieved with the following..

    It’s two different things. memsetting to 0 means you can use memcmp and not worry about padding. The author’s way doesn’t guarantee that memcmping two structs with the same values in fields would return the expected result.

    1. 9

      caveat emptor: this does not fill the padding with zeros, and depending on your situation, may or may not be a big deal f.e. in networking code, it might lead to information leakage.

      1. 4

        Sending struct representations over the network directly sets off pretty big alarm bells for me. It’s usually worth the effort to (de)serialize properly—mostly because you don’t have to worry about the compiler changing your structs in some way, but also because network representations tend to have slightly different requirements from in-memory ones anyway.

        1. 2

          Have you ever called stat()? How do think the kernel filled in struct stat?

          1. 2

            I have, but as far as I know I’ve always been talking to the local kernel, not one at the other end of some network.

            I might have said something other than “network”, though, I guess. I meant to talk about programs that send struct representations to different computers where the ABI might be different.

            1. 6

              Sorry for being unclear. The kernel is a separate security domain as well. Leaking padding bytes is a problem there. I think this frequently overlooked. Concentrating only on network protocols ignores other aspects why you still should memset everything.

              1. 1

                That’s fair. I guess maybe there’s an argument that we should think of the connection to the kernel as more like a network link in general.

      2. 7
        if(NULL != ptr)
            free(ptr);
        

        To me, that is usually a bit of code that says “I don’t know what I’m doing”.

        Whoever wrote that code doesn’t know whether they had allocated or not and is doing “frantic checking” of everything.

        Instead of designing it to work, instead of designing it to only free stuff that was allocated, the programmer is faced with a large chunk of code he doesn’t understand, and is frantically checking everything instead of trying to understand what it does and should do.

        1. 3

          I think part of the reason is the emphasis many people put on portability of C code. For instance, until recently there was one major compiler vendor who did not support large parts of C99 including designated initializers. Also, some people may be forced to use arcane versions of GCC or proprietary compilers for embedded platforms, which might not support C99.

          A problem with designated initializers specifically is that they are not part of C++ (at least until C++20), so they are not ideal for example code which might be picked up by both C and C++ programmers.

          An interesting aspect is that compilers are aware of old idiomatic ways to do things and tend to optimize them so you end up with the same code when optimization is turned on.

          Don’t get me wrong, I am all for moving forward, and there are a lot of things in C99 I wouldn’t like to be without (// comments and declaring variables anywhere to name two), but sometimes I still write ANSI C for maximum portability.

          1. 3

            The last bit is the only thing I like here. If one up it and say putting ptr = NULL after the free is good practice in many cases.

            I’d probably reject any change trying to introduce the first two though.

            1. 1

              I’d probably reject any change trying to introduce the first two though.

              Without even a hint of the reason for doing that, this feels to me like argument from authority.

              1. 3

                Am I required to like changes because somebody writes a blog post about them?

                1. 4

                  The blog post expresses an opinion, but it is an opinion on a technical matter and it does provide arguable reasons for preference of one way of doing something over another. I admit it might have been pretentious of me to expect disagreements to be based on reasoning as well, but I don’t think anything else leads to productive conversation.

            2. 2

              And finally, there is absolutely no reason to check if a pointer is NULL just before calling free() on it […] Quoting the standard: “If ptr is a null pointer, no action occurs.”

              It’s a lot more helpful to know that it’s section 7.20.3.2 (p. 313) of the linked document, which itself has no links because it a draft version.

              1. 2

                Not to be confused with checking for pointers being non-null in goto-based error handling code.

                1. 2

                  Posts like this one are why I would like an Incorrect or Unproven category for flagging.

                  I agree about removing null checks but the author doesn’t provide compelling reasons for the use of designated initializers or compound literals.

                  1. 5

                    C89 allows one to initialize a structure:

                    struct flags f =
                    {
                      false,
                      false,
                      false,
                      true,
                      true,
                      false,
                      false,
                    };
                    

                    But you have to check the struct flags declaration to figure out what is being intialized to what. With designated initializers, this turns into:

                    struct flags f = 
                    {
                      .fullurl    = false,
                      .reverse    = false,
                      .navigation = false,
                      .navprev    = true,
                      .navnext    = true,
                      .edit       = false,
                      .overview   = false,
                    };
                    

                    which gives better context, and the fields don’t have to be initialized in the order of definition, so this could be cut down to:

                    struct flags f = 
                    {
                      .navprev = true,
                      .navnext = true,
                    };
                    

                    with the other flags being false (set to 0). This is great if there’s a few fields you want to preinitialize and they aren’t at the start of the structure (and the above example is from code I’ve written).

                    Compound literals I don’t have as much of an argument for. I’ve used them, and I’ve done one of the examples given:

                    setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &(int) {true}, sizeof(int));
                    

                    and consequently, I do like them, but I can see why one might not. For me, if there’s a one-off structure or pointer where I would otherwise have to create a variable, then I will probably use a compound literal, since I’m not a real fan of variable declarations in the middle of code (I’m a long time C programmer, and old habits die hard).

                    1. 4

                      You make a good case for designated initializers. The reason I avoid them is that they aren’t C++ compatible yet. I like to compile my C projects as C++ for slightly stronger type checking. C++20 adds designated initializers and I will consider using them when it becomes mainstream. We have opposite variable declaration styles. I declare my variables with the smallest possible scope and use single assignment whenever I can do so.

                    2. 4

                      I will go further and say that the compound literal thing seems like very bad advice to me. the ‘before’ code was a lot more readable and you get to document the constant via the variable name.

                      1. 2

                        My rule of thumb for language features is to ask myself the following question.
                        “How would I feel if the feature was new to me and I had to fix a bug in the middle of the night?”

                        The only obscure C# feature I use at work is combining expression bodies with tuple deconstruction.
                        This idiom is optimized by the compiler so there is no performance penalty for using it.

                        public sealed class ReportArgument
                        {
                            public ReportArgument(string name, string value) =>
                                (Name, Value) = (name, value)
                            
                            public string Name { get; }
                            
                            public string Value { get; }
                        }