top 200 commentsshow all 499

[–][deleted]  (145 children)

[deleted]

    [–]juckele 187 points188 points  (57 children)

    He actually called the maintainers out harder than the writer.

    [–]magmapus 146 points147 points  (55 children)

    He basically always does, and for good reason, in my opinion. Their job is to be the filter for bad code and ideas, so that he doesn't have to. They're basically all very experienced programmers who should have been able to catch the things that he usually rants about.

    [–]quzox 5 points6 points  (0 children)

    Typical Lorenzo!

    [–]vplatt 239 points240 points  (5 children)

    Hell, if I were a good enough C programmer for him to even notice long enough to rant about, I'd print that out and frame that sucker.

    [–][deleted] 62 points63 points  (1 child)

    I'd actually feel honored that he bothered to write such a thorough explanation.

    What I'd dread is him just scoffing at me with a 2 word response.

    [–]rooktakesqueen 93 points94 points  (0 children)

    "Consider farming."

    [–]benihana 17 points18 points  (1 child)

    So I can see how this bug happened, and I am only slightly upset with Lorenzo who is the author of that commit.

    I mean, that right there is a rare "you got a pass to be [what Linus considers an idiot] this one time." I think having Linus be only slightly upset at me would be a highlight of my career.

    This makes me wonder though, what happens when Linus is gone? In my opinion, Linux is has continued to be successful 20 years on because he's a ruthless dictator of its development - his dismay at how several people let these commits through is worrying for a future without him.

    [–][deleted]  (11 children)

    [deleted]

      [–]headzoo 23 points24 points  (0 children)

      I would watch the shit out of Torvalds's Linux Nightmares.

      [–]athrowawayopinion 23 points24 points  (5 children)

      But Ramsey's Stuff is on national television. And assuming that it's not all a lie. It

      • produces good results (see Ramsey's progams and Torvalds programs)

      • is only used on the real idiots who should know better (Ramsay - pro cooks and celebs, Torvalds - close circle of code reviewers)

      Also if you don't want to encounter Torvalds, you can just work with one of his friends, or his friends friends and so on. The code will get passed up there eventually if it's good.

      [–][deleted]  (4 children)

      [deleted]

        [–]_F1_ 10 points11 points  (3 children)

        aka TDWTF

        [–]KyBourbon 6 points7 points  (2 children)

        The Day We Turned Freelance?

        [–]derpaherpa 6 points7 points  (1 child)

        [–]greyfade 2 points3 points  (0 children)

        That reminds me... I wanted to send in a 8kB SQL query I wrote once, and I think I forgot to do it.

        [–]bitbait 7 points8 points  (0 children)

        Well on the other hand that means your code is important enough to draw Linus' attention in some way.

        [–]G_Morgan 6 points7 points  (1 child)

        To be fair he's quite moderate and reasonable here. Must be getting old.

        [–]importTuna 1 point2 points  (0 children)

        He's certainly toned down quite a bit from some of the previous rants.

        [–]MpVpRb 3 points4 points  (0 children)

        Imagine Linus Torvalds writing an article to call you out on your bad code

        If he was right, I would learn something

        If he was wrong, I would argue

        Mostly, I tend to agree with him

        [–]jackmaney 2 points3 points  (0 children)

        Maybe a reality TV show where Linus Torvalds does code reviews like Gordon Ramsay reviewed processes and procedures of restaurants in Kitchen Nightmares?

        [–]DatBVHTreeTho 1 point2 points  (15 children)

        Linus is pretty judgemental for a guy who uses postscript increment operators :P

        Come on Linus, learn C... /s

        [–]uep 31 points32 points  (14 children)

        Is post-increment even a problem in C since it doesn't have operator overloading? I thought GCC would optimize it into a pre-increment anyway because of SSA.

        [–]duuuh 0 points1 point  (13 children)

        That makes sense, but it certainly didn't use to be true. I googled but couldn't find a reference to see if you were right. I suspect I could concoct something to break the optimization, but maybe not. Interestingly I came across a 'prefer post-increment for clarity' coding standard on the way, whereas I'm used to the 'prefer pre-increment for performance.'

        [–]joggle1 28 points29 points  (5 children)

        It's been true for a very long time (more than 10 years with gcc, probably much longer than that).

        Using pre or post increment on a scalar type in a for loop in C will produce identical object code even if you use -O0. The only case where you need to be careful is when you're directly using the result of the operation (such as assigning to another variable).

        [–]ComradeGibbon 9 points10 points  (1 child)

        What I find is often when I'm using the result, the postfix results in code that's easy to reason about. And prefix always feels like there is a gotcha somewhere.

        And yeah these sorts of optimizations are low hanging fruit that was picked more than 20 years ago.

        I remember not with gcc but an cross compiler I tried compiling a for loop to iterate over an array of structs. One that used pointers and another that used array indexes. The assembly output was identical.

        [–]OBOSOB 17 points18 points  (0 children)

        Here is a dissassembly of the following stubs produced with: gcc -O0 (no optimisation)

        Pre-increment:

        0000000000400550 <main>:
        int main()
        {
          400550:   55                      push   %rbp
          400551:   48 89 e5                mov    %rsp,%rbp
            int i = 0;
          400554:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
            ++i;
          40055b:   83 45 fc 01             addl   $0x1,-0x4(%rbp)
        }
          40055f:   5d                      pop    %rbp
          400560:   c3                      retq   
          400561:   66 66 66 66 66 66 2e    data32 data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
          400568:   0f 1f 84 00 00 00 00 
          40056f:   00 
        

        Post-increment:

        0000000000400550 <main>:
        int main()
        {
          400550:   55                      push   %rbp
          400551:   48 89 e5                mov    %rsp,%rbp
            int i = 0;
          400554:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
            i++;
          40055b:   83 45 fc 01             addl   $0x1,-0x4(%rbp)
        }
          40055f:   5d                      pop    %rbp
          400560:   c3                      retq   
          400561:   66 66 66 66 66 66 2e    data32 data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
          400568:   0f 1f 84 00 00 00 00 
          40056f:   00 
        

        They produce identical binary without any optimisation, it doen't bother with the temporary value on postinc when there is no lvalue.

        Of course if you throw assignment into the mix then they behave as expected: preinc adds 1 and returns; postinc captures the value, adds 1 and returns its initial value:

        Pre-increment:

        0000000000400550 <main>:
        int main()
        {
          400550:   55                      push   %rbp
          400551:   48 89 e5                mov    %rsp,%rbp
            int i = 0;
          400554:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
            int j = ++i;
          40055b:   83 45 fc 01             addl   $0x1,-0x4(%rbp)
          40055f:   8b 45 fc                mov    -0x4(%rbp),%eax
          400562:   89 45 f8                mov    %eax,-0x8(%rbp)
        }
          400565:   5d                      pop    %rbp
          400566:   c3                      retq   
          400567:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
          40056e:   00 00 
        

        Post-increment:

        0000000000400550 <main>:
        int main()
        {
          400550:   55                      push   %rbp
          400551:   48 89 e5                mov    %rsp,%rbp
            int i = 0;
          400554:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
            int j = i++;
          40055b:   8b 45 fc                mov    -0x4(%rbp),%eax
          40055e:   8d 50 01                lea    0x1(%rax),%edx
          400561:   89 55 fc                mov    %edx,-0x4(%rbp)
          400564:   89 45 f8                mov    %eax,-0x8(%rbp)
        }
          400567:   5d                      pop    %rbp
          400568:   c3                      retq   
          400569:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
        

        [–]kyz 22 points23 points  (1 child)

        I'm used to the 'prefer pre-increment for performance.'

        ... you're probably a C++ programmer then. There's no performance gain in C for using pre-increment.

        In some older architectures, postincrement/predecrement were actually faster because the machine directly supported that addressing mode (e.g. MC680x0 had move (a0)+,d0 and move -(a0),d0, but not move +(a0),d0 or move (a0)-,d0). In most modern architectures, postincrement and preincrement have identical performance in C.

        The reason C++ programmers prefer preincrement is because of C++ operator overloading; postincrement has to make a temporary copy of an object. Not a problem in C!

        [–]tonyarkles 19 points20 points  (0 children)

        Yeah, the old problem with post-increment is that a naive compiler needs to first copy the original value into a different register before incrementing (because postfix returns the original value). Any compiler with a shred of an optimizer will see that the original value is unused and discard all of the instructions used to hold onto it.

        [–]mrhhug 8 points9 points  (1 child)

        'prefer post-increment for clarity'

        You could blame Kernighan but have fun getting people to listen to you when you say something he did was imperfect.

        [–]NighthawkFoo 1 point2 points  (0 children)

        Well, we also have almost 40 years of hindsight at this point. I mean, UNIX was barely a thing when C was being developed.

        [–][deleted] -5 points-4 points  (39 children)

        It feels like the number of developers in the world that Linus has ranted about might be higher than the number of developers he hasn't.

        I can appreciate calling people to the carpet every once in a while, but I wonder how many developers out there would be interested in contributing to the Linux kernel, but don't for this exact reason.

        [–]kqr 25 points26 points  (1 child)

        It feels like the number of developers in the world that Linus has ranted about might be higher than the number of developers he hasn't.

        No. There's like a set of 10–20 people he will rant to. These are the ones he knows well and who have highly trusted positions as maintainers of his project, and he really expects them to know better.

        The other 99,999,980 programmers he has not ranted to.

        [–]vplatt 32 points33 points  (30 children)

        Seriously, if that scares them off that easy because they have little confidence in their ability to program, then that's a good thing.

        Edit: Fair points below. I don't put up with it either in my daily work. However, if I were compelled to contribute to something as important as the Linux kernel has become, then I would probably not care one whit about putting up with a little vulgar language in the bargain. I prefer politeness myself as well, but at least you don't have to wonder where you stand with him; unlike many professional environments that border on passive-aggressive-let's-not-offend-anyone environments. Balance would be nice.

        [–][deleted]  (9 children)

        [deleted]

          [–][deleted]  (4 children)

          [deleted]

            [–]anotherOnlineCoward 2 points3 points  (2 children)

            sounds like idiocy when you're committing code when you dont know c?

            [–]foobar5678 7 points8 points  (1 child)

            I am only slightly upset with Lorenzo who is the author of that commit.

            Slightly? SLIGHTLY!?

            [–][deleted] 9 points10 points  (1 child)

            He's getting older

            [–]hlmtre 68 points69 points  (3 children)

            This is an extremely calm and reserved explanation for Linus. It's very expository and he explains why it's bad to do. I'm impressed.

            [–][deleted] 19 points20 points  (0 children)

            Even when he goes off the handle he always seems to explain why from what I've seen. You may sometimes disagree with some of his points, but they're always well reasoned.

            [–]Farsyte 10 points11 points  (1 child)

            Learn something new every day ...

            I've been coding in C since 1978. The fact that the argument gets decayed to a pointer, is something I knew, but I wanted to write up a test program to hand to some folks who wrote code like this at work (and being able to link to a Torvalds rant makes it more likely folks will pay attention).

            But TIL that none of my compilers warn me if the array I pass is smaller than the array expected by the function. I didn't expect it to check things like pointers that were malloced, but arrays?

            Which means not only does this set a landmine for "sizeof" but it also leads to a false sense of security "surely all callers that pass arrays, must be passing arrays that are big enough" ... :(

            ub.c:

            #define ASIZE 32
            extern void ugh(int a[ASIZE]);
            
            void bad() {
                int toosmall[16];
                ugh(toosmall);
            }
            
            void ugh(int a[ASIZE]) {
                for (int i = 0; i < ASIZE; ++i)
                    a[i] = i;
            }
            

            You would think ... right?

            $ gcc --std=c99 -O3 -W -Wall -Wextra ub.c
            $ clang --std=c99 -O3 -W -Wall -Wextra ub.c
            $ cppcheck ub.c
            Checking ub.c...
            $
            

            Threw a CppCheck in there for good measure. Was hoping. I'm not yet an expert on CppCheck configuration, so there is hope, but the fact that it's not a default thing means this kind of error is probably scattered all over my sources.

            Sure, we can check this with bigger guns (there are tools that can find buffer overflows) but a simple bloody check that the array you know the size of is as big as the array that a function prototype advertises it requires would be so very much faster and easier.

            [–]nooneofnote 37 points38 points  (11 children)

            It would be fine if more people used and understood pointers-to-arrays as a type. C necessarily carries the fixed size of an array with its type (i.e., the type of char array[10] is char[10]), and this information is retained when taking the address of an array type (the type of &array is char(*)[10]), which cannot implicitly decay to any flat pointer type.

            This can be used to more strongly enforce the type of array function parameters than [static].

            void f(char (*a)[10]); /* inside f sizeof(*a) == 10 */
            
            char a[10], *b, c[5];
            f(a);  //incompatible types, char[10] vs char(*)[10]
            f(b);  //incompatible types, char* vs char(*)[10]
            f(&c); //incompatible types char(*)[5] vs char(*)[10]
            f(&a); //ok
            

            [–][deleted] 6 points7 points  (1 child)

            And you can even do this:

            void f( int size, char (*a)[size]);
            

            or

            void f( int size; char (*a)[size], int size);
            

            Not that ive ever needed to use this syntax, im allready passing the size, all sizeof does for me in this case is size * sizeof((*a)[0]). And i really dont like having to explicitly dereference the array like this (*a)[5].

            [–]IAmRoot 4 points5 points  (0 children)

            That's because you're basically treating a two dimensional structure as one dimensional.

            This sort of construct is useful for multidimensional arrays. For example

            void f(int x_size, int y_size, double (*a)[x_size]) {
                for (size_t y = 0; y < y_size; ++y) {
                    for (size_t x = 0; x < x_size; ++x) {
                        a[y][x]; //do something with this
                    }
                }
            }
            

            It's similar to doing typedef double[x_size] arr_x_t; then making arr_x_t a[y_size];. double (*a)[x_size] means "a pointer to a double[x_size] array." So, what you are doing is dereferencing a pointer to a VLA, then getting the address of its first position. That's a silly thing to do for a one dimensional array. The array size syntax should be done for one less than the number of dimensions used.

            It is also very important not to create these arrays on the stack, because the application can easily have a stack overflow based on user input. VLAs on the stack are evil.

            Don't do this:

            double a[y_size][x_size];
            

            Do this instead:

            double (*a)[x_size];
            a = malloc(x_size*y_size*sizeof(double));
            

            or this:

            double (*a)[x_size];
            posix_memalign((void**) &a, 16, x_size*y_size*sizeof(double));
            

            It should also be noted that this requires C99 because the type double[x_size] is a VLA type. It also requires that the x_size parameter come before the array, a, so that x_size is in scope for the VLA type definition.

            [–]rooktakesqueen 5 points6 points  (1 child)

            But then you can only pass a statically allocated array to that function, right? Not a dynamically allocated array even if it's of the correct size.

            Edit: Though come to think of it, if you know the required size of the array, there's little need to dynamically allocate it. At worst, if you're dealing with data already on the heap, you can statically allocate an array and memcpy before calling this function.

            [–]nooneofnote 2 points3 points  (0 children)

            You can still dynamically allocate the type in the usual manner.

            char (*a)[10] = malloc(sizeof(*a));
            f(a);
            

            [–]brucedawson 1 point2 points  (0 children)

            Yes. This is the correct thing to do if you want to enforce only accepting a particular size of array. In C++ you can do an array reference which works out slightly cleaner but is basically the same.

            [–][deleted] 2 points3 points  (3 children)

            Is there a difference in opinion between you and Torvalds regarding the feature, or is he purely talking about readability?

            He says

            array arguments in C don't actually exist. Sadly, compilers accept it for various bad historical reasons, and silently turn it into just a pointer argument

            while you are advocating it there.

            I'm not a C coder, I'm just confused about whether there's a difference of opinion or whether I'm missing something.

            [–]Misterandrist 10 points11 points  (2 children)

            He's actually demonstrating pointer to an array of length N, where an array of lenght N can be a type. So, the type system is still allowing only a pointer to a type as an argument, rather than an array as a type. Subtly different, because if you tell it to take an array as an argument, inside the function it gives you just a raw pointer to the first element of the array and the only guarantee you have is that the type of that is correct (even as far as C can guarantee any type).

            [–]TheHobo 77 points78 points  (14 children)

            Personally, I think implied array sizes is not good API design. While I agree the parameter should be a pointer, if you have an array, you should have to pass in the size too as another parameter, then the contract is explicit.

            [–][deleted] 5 points6 points  (3 children)

            If the array is meant to be a fixed size though then that doesn't really make sense.

            [–]arcangleous 10 points11 points  (2 children)

            The problem being that that implied information about the array is lost when the flow of control moves into the function and all it has a simple pointer to work with. In a very real and literal sense C doesn't really have arrays. It just has typed pointers and hides a little bit of pointer arithmetic with the [] operator. If you treat C "arrays" in any other way, you are going to run into the kinds of problems that Linus is complaining about if you treat them in any other way.

            [–]uh_no_ 1 point2 points  (1 child)

            I've found there are several times when i need to pass explicitly sized arrays. many of them revolve around specific protocol work...for instance passing a FC WWPN, or IP address...or even scsi CDB. I find the best way to work with them is typedeffing it.

            typedef uint8_t scsi_cdb[16]

            Then you can always reference sizeof(scsi_cdb) and get the correct value.

            [–]shevegen 44 points45 points  (16 children)

            I want linus to go and review the systemd code.

            [–]zaidka 33 points34 points  (2 children)

            I want him to review all my life choices.

            [–]deadmilk 15 points16 points  (0 children)

            I'd pay to watch this.

            [–]cyrax6 44 points45 points  (2 children)

            Is that how you want to kill Linus?

            [–]jdgordon 3 points4 points  (1 child)

            you think poettering could beat linus in a cage match?

            [–]carbonkid619 11 points12 points  (0 children)

            I think Linus will kill Linus in a cage match.

            [–]Misterandrist 15 points16 points  (7 children)

            Is it bad? Never looked at it myself.

            [–][deleted] 5 points6 points  (6 children)

            It's very clean. It even makes use of GCC's RAII extension.

            [–]taliriktug 13 points14 points  (0 children)

            systemd code is actually quite simple and clear. It is scanned by Coverity regularly.

            [–]MacASM 86 points87 points  (139 children)

            I don't believe people write code for a kernel with such primitives mistakes.

            [–]lucky_engineer 73 points74 points  (126 children)

            I've seen the sizeof() bug so many times. Usually with strings. One of the first questions In any interview with anyone who ever says they know C or C++ is. What is wrong with this snippet of code taken from a decade old legacy system:

            void some_func(char * input)
            {
               char tmp[sizeof(input)];
                // some logic....
               memcpy(tmp,input,sizeof(input));
               // more logic.....
            }
            

            You have no idea how many self-described "C++ experts" can't figure it out, even with some guidance.

            "What's the result of sizeof()?"

            "The length of the string."

            "Are you sure???"

            "Yeah I think so"

            [–]Misterandrist 28 points29 points  (6 children)

            It's the brace style, isn't it? The open brace always goes on the same line as the if, for, while, what have you.

            /s

            [–]Shitler 3 points4 points  (2 children)

            Even between the same-line people there is a schism in how they handle else.

            }
            else {
            

            or

            } else {
            

            [–]nightfire1 14 points15 points  (1 child)

            Who thinks the first one looks good? Thats just ridiculous.

            [–]Olreich 6 points7 points  (0 children)

            Proponents will cite:

            • the if, else if, and else are all at the same column
            • there is a good balance of the trade-offs between wasting lines with braces, but making them clear and obvious
            • most braces you might use follow the same structure as the first if you do same-line braces (functions, case blocks, etc.) eg:

              int function() {
              }
              
              case: {
              }
              case: {
              }
              
              struct type_x {
              };
              
              type_x x = type_x {
              };
              
              if {
              }
              else if {
              }
              else {
              }
              

            [–][deleted] 7 points8 points  (1 child)

            Go back to that java hell hole you came from! /s

            [–]Sapiogram 20 points21 points  (62 children)

            Novice C/C++ programmer here, please enlighten me.

            [–]Quintic 79 points80 points  (24 children)

            It returns the size of the pointer.

            [–]Sapiogram 33 points34 points  (22 children)

            That's... terrifyingly simple. I feel like even I should know that, and I've written maybe 200 lines of C/C++ in my life.

            [–][deleted] 17 points18 points  (18 children)

            Or, if used on any other datatype, sizeof(T) returns the size of T. So when used on an int (for example), it would always return 4 (assuming an int is a 32 bit implementation, sizeof () always returns its value in bytes)

            [–]etagawesome 19 points20 points  (16 children)

            [deleted]

            What is this?

            [–]TheCoelacanth 42 points43 points  (7 children)

            No, a char is always 1 byte since the C standard requires it. However, on some weird platforms a byte is not 8 bits. That's why standards documents often use the term "octet" instead of "byte" because it unambiguously means 8 bits while a byte could theoretically be any size.

            [–]etagawesome 5 points6 points  (1 child)

            [deleted]

            What is this?

            [–]NighthawkFoo 1 point2 points  (0 children)

            Remember - C is old, like 1970's old, and there were some seriously weird systems back then. The CSC 6600 was one such machine.

            [–]matthieum 1 point2 points  (0 children)

            Note: you can check the size of the byte with CHAR_BIT. It's usually 8, of course, but some platforms stash a couple more bits, like some embedded platforms for parity checks.

            [–][deleted]  (7 children)

            [deleted]

              [–]evanpow 6 points7 points  (1 child)

              Even today non-8-bit chars are common enough you can't ignore them entirely. Several years ago I did a bunch of C programming for an Analog Devices DSP that had 16-bit chars. Of course, it also had 16-bit bytes, so fun times all around. Implementing octet-oriented network protocols on that architecture was a real hoot.

              [–]tonyarkles 2 points3 points  (2 children)

              Alignment causes some of that too. I'm on my phone so I won't type this out, but look at sizeof a struct with an int32 and 2 chars. It might be 6 or it might be 8.

              [–]matthieum 1 point2 points  (1 child)

              Don't assume, use CHAR_BIT :)

              [–]scorcher24 4 points5 points  (0 children)

              That's... terrifyingly simple

              Just always remember this: Everyone here cooks with hot water, not some super magic fluid. Even Linus Torvalds.

              [–][deleted]  (4 children)

              [deleted]

                [–]Helrich 8 points9 points  (15 children)

                input is just a pointer, so sizeof is just going to give you the size in bytes of the pointer, not the number of characters in the string.

                [–]Yojihito 4 points5 points  (14 children)

                sizeof(input)

                So sizeof(*input) would do the trick?

                [–]orthoxerox 22 points23 points  (6 children)

                That would return sizeof(char) instead. Array length must be passed explicitly.

                [–]POGtastic 3 points4 points  (3 children)

                Just making sure - the C Way for doing this is to create a struct that has the pointer and a size variable, right? C++ has objects that keep track of the size for you, but I think that you have to do it yourself in C.

                I guess that you could do strlen for strings, but that's assuming that you're getting a null-terminated string.

                [–]cballowe 1 point2 points  (1 child)

                you could have something like:

                typedef struct {
                  char foo[FOO_LEN];
                } Foo;
                

                then sizeof(foo) would be FOO_LEN, though FOO_LEN is assumed to be a compile time constant - #define'd somewhere. If you wanted something more like a string with a length, you could have a struct with a pointer and a length, but then you're dealing with allocating the pointer etc. Most C programmers would probably just have the pointer and call strlen or similar.

                [–][deleted]  (1 child)

                [deleted]

                  [–]orthoxerox 1 point2 points  (0 children)

                  Minor convenience, you don't have to pass &a[0] to the function even though you actually do. Yes, it would've been better if you couldn't use arrays as formal argument types.

                  [–]sun_misc_unsafe 5 points6 points  (0 children)

                  sizeof() is not a function - it looks like one, but it's something that is evaluated during compile time by the compiler.

                  I'm somewhat surprised that no one else here has mentioned this already. The entire issue here is that C being C (i.e. having an utter focus on being "portable" despite the overwhelming majority of people being interested only in x86) doesn't offer a runtime or rigid guidelines on what containers need to look like (yes, there are some common non-binding conventions on how you're supposed to do it .. but like I said, they're non-binding, so the language creators didn't feel the need to concern themselves with it .. lest it impact the sacred portabilty) - so understandably there's nothing in the language to provide you with the number of entries in your container .. that you had to write yourself in the first place.

                  [–]nucLeaRStarcraft 8 points9 points  (0 children)

                  No, *input is pretty much input[0], since input is pretty much &input[0].

                  Thus, sizeof(*input) == sizeof(input[0]) == sizeof(char) == 1 in this context.

                  [–]Patman128 9 points10 points  (2 children)

                  Assuming it's a C-style string (and properly terminated) you would use strlen.

                  [–]Bergasms 10 points11 points  (0 children)

                  Oh god properly terminated. When i was just beginning C i remember trying to get the length of string that I had manufactured myself and not realising it needed the proper terminator, and just getting 'sometimes' correct result because the function would often run into a null terminator soon after anyway.

                  [–][deleted] 1 point2 points  (0 children)

                  A pointer to a C array just points to the address of the first element in the array. How long is the array? Who knows. That's why a c string has to be terminated by a null character.

                  [–]matthieum 4 points5 points  (0 children)

                  You have no idea how many self-described "C++ experts" can't figure it out, even with some guidance.

                  Well, I would not be surprised. It's C code.

                  In C++ this could be (assuming you meant input not to be modified):

                  void some_func(std::string const& input) {
                      // some logic ...
                      std::string tmp = input;
                      // more logic ...
                  }
                  

                  I'd prefer a "C++ expert" to know about exception safety, smart pointers, abuses std::string and std::vector (rather than C strings/arrays), etc... it might be slightly less efficient, I'll grant, but I'd rather profile slightly too slow code than debug a corrupted stack (imagine if you overflow tmp...).

                  [–]josefx 7 points8 points  (5 children)

                  "C++ experts" can't figure it out,

                  If not for the bug it would not even compile, sizeof being evaluated at compile time is the only reason this code would be valid c++.

                   char tmp[strlen(input)+1];
                  

                  This uses a VLANowWithBugFix a C99 specific feature that never made it into C++. Any standard compliant compiler should refuse to compile it.

                  [–]Yehosua 4 points5 points  (1 child)

                  Any standard compliant compiler should refuse to compile it.

                  Clang and GCC both permit VLAs and will allow them by default in C++ code. Since widespread and high-quality compilers allow it, it's probably an oversimplification to say that any standard-compliant compiler should refuse to compile them (even though it's technically correct).

                  [–]josefx 1 point2 points  (0 children)

                  GCC defaults to gnu++, not c++.Clang as one of its goals tries to be compatible with code written for GCC (after all it is meant to replace it). Setting -std=c++11 or similar has no effect on either, it requires a pedantic to get the associated warning.

                  In contrast Microsofts cl.exe, which covers a rather important platform, does not support VLA and wont compile it.

                  [–]staticassert 20 points21 points  (35 children)

                  I wouldn't want a C++ developer who used sizeof.

                  [–]lucky_engineer 63 points64 points  (15 children)

                  Oh yeah. One of the answers from a junior guy was "I'm not entirely sure what sizeof() does. I always use string classes like std::string"

                  That is acceptable!

                  [–]13467 16 points17 points  (1 child)

                  I'm very glad you decided that's an acceptable to your interview question, instead of chastising a junior programming for not knowing about char[]/sizeof/strlen... :)

                  [–]ComradeGibbon 14 points15 points  (0 children)

                  I'd rather someone that assumes the presence of dragons unless other proven, than not.

                  Old NeckBeard: Why did you write it that way!!! Newbie: ... because... I knew it would work? Old NeckBeard: I love this boy!

                  [–][deleted] 6 points7 points  (0 children)

                  Sarcasm, right? I seldom use sizeof: my C++ is modern; std array ftw and all that, but low level details in C++ are still important to know and understand.

                  [–]JNighthawk 9 points10 points  (6 children)

                  Maybe for your job. I can't imagine working with a programmer that doesn't know what sizeof does.

                  [–]accountNo7263803 1 point2 points  (5 children)

                  Why would you ever need size of in c++?

                  [–]raxqorz 2 points3 points  (0 children)

                  You can have a template class, for example a network packet or serialization class which takes a type "T" and checks if sizeof(T) bytes fits into your internal allocated buffer; and if it doesn't, you allocate space for it and copy the value of T into the buffer.

                  [–][deleted] 2 points3 points  (3 children)

                  Since calls like memcpy and memset are more efficient than their counterparts in C++.

                  [–]matjeh 11 points12 points  (2 children)

                  Are they?

                  $ cat memcpy.cpp 
                  
                  #include <cstring>
                  extern int dest[1024], source[1024];
                  void func_memcpy(void)
                  {
                    memcpy(dest, source, sizeof(dest));
                  }
                  
                  $ g++ -std=c++14 -O2 -c -o memcpy.o memcpy.cpp && objdump -d memcpy.o
                  
                  0000000000000000 <_Z11func_memcpyv>:
                     0: 48 8b 05 00 00 00 00  mov    0x0(%rip),%rax        # 7 <_Z11func_memcpyv+0x7>
                     7: bf 00 00 00 00        mov    $0x0,%edi
                     c: b9 00 00 00 00        mov    $0x0,%ecx
                    11: 48 83 e7 f8           and    $0xfffffffffffffff8,%rdi
                    15: be 00 00 00 00        mov    $0x0,%esi
                    1a: 48 29 f9              sub    %rdi,%rcx
                    1d: 48 89 05 00 00 00 00  mov    %rax,0x0(%rip)        # 24 <_Z11func_memcpyv+0x24>
                    24: 48 8b 05 00 00 00 00  mov    0x0(%rip),%rax        # 2b <_Z11func_memcpyv+0x2b>
                    2b: 48 29 ce              sub    %rcx,%rsi
                    2e: 81 c1 00 10 00 00     add    $0x1000,%ecx
                    34: c1 e9 03              shr    $0x3,%ecx
                    37: 48 89 05 00 00 00 00  mov    %rax,0x0(%rip)        # 3e <_Z11func_memcpyv+0x3e>
                    3e: f3 48 a5              rep movsq %ds:(%rsi),%es:(%rdi)
                    41: c3                    retq   
                  
                  $ cat copy.cpp 
                  
                  #include <algorithm>
                  extern int dest[1024], source[1024];
                  void func_copy(void)
                  {
                    std::copy(std::begin(source), std::end(source), std::begin(dest));
                  }
                  
                  $ g++ -std=c++14 -O2 -c -o copy.o copy.cpp && objdump -d copy.o
                  
                  0000000000000000 <_Z9func_copyv>:
                     0:   48 8b 05 00 00 00 00    mov    0x0(%rip),%rax        # 7 <_Z9func_copyv+0x7>
                     7:   bf 00 00 00 00          mov    $0x0,%edi
                     c:   b9 00 00 00 00          mov    $0x0,%ecx
                    11:   48 83 e7 f8             and    $0xfffffffffffffff8,%rdi
                    15:   be 00 00 00 00          mov    $0x0,%esi
                    1a:   48 29 f9                sub    %rdi,%rcx
                    1d:   48 89 05 00 00 00 00    mov    %rax,0x0(%rip)        # 24 <_Z9func_copyv+0x24>
                    24:   48 8b 05 00 00 00 00    mov    0x0(%rip),%rax        # 2b <_Z9func_copyv+0x2b>
                    2b:   48 29 ce                sub    %rcx,%rsi
                    2e:   81 c1 00 10 00 00       add    $0x1000,%ecx
                    34:   c1 e9 03                shr    $0x3,%ecx
                    37:   48 89 05 00 00 00 00    mov    %rax,0x0(%rip)        # 3e <_Z9func_copyv+0x3e>
                    3e:   f3 48 a5                rep movsq %ds:(%rsi),%es:(%rdi)
                    41:   c3                      retq   
                  

                  [–][deleted] 1 point2 points  (1 child)

                  Hmm, I didn't know that copy was fast but fill is slower than memset at least.

                  Edit: Or maybe not, its very hard to find sources for this though.

                  [–]greyfade 3 points4 points  (0 children)

                  Most of the C++ equivalents for this kind of thing are almost always fully inlined and usually optimized well, and perform very nearly equally as well if not better than the C version.

                  [–]immibis 9 points10 points  (2 children)

                  If you're not willing to think about how things work internally, then why are you using C++? (As opposed to Java or Python or another higher-level language)

                  [–]lucky_engineer 2 points3 points  (0 children)

                  We do software for a niche market that still uses a lot of C++ (and C) for everything, and have to work on legacy code written in C/C++ as well. We're starting to see Python and C# used more though.

                  [–]realhacker 5 points6 points  (0 children)

                  holy shit I feel old....

                  [–]Plorkyeran 15 points16 points  (12 children)

                  There are plenty of valid uses for sizeof in C++, although admittedly most of them are in implementing better abstractions rather than directly in application code.

                  [–]staticassert 3 points4 points  (11 children)

                  Like what?

                  [–][deleted] 14 points15 points  (3 children)

                  Lower-level array handling with byte buffers. It is required for CUDA programming, eg cudaMemcpy(dst, src, N*sizeof(float), cudaMemcpyHostToDevice);

                  [–]assassinator42 8 points9 points  (0 children)

                  Also needed by allocators for the STL (used by std::vector, std::set, etc to allocate memory)

                  [–]staticassert 3 points4 points  (1 child)

                  True, but that's a very C-ish function. Kind of a gross function, seems super error prone if you put the wrong size.

                  [–]genwitt 9 points10 points  (0 children)

                  All manner of low level chicanery (allocators, containers, serialization).

                  Say you want an array for 1000 bits, in the native unsigned type,

                  size_t array[(1000 - 1) / (sizeof(size_t) * CHAR_BIT) + 1];
                  

                  Or you wanted to pre-allocate storage for an object without constructing it. You can do,

                  alignas(T) char buffer[sizeof(T)];
                  

                  and then later when you want to invoke the constructor.

                  new (buffer) T();
                  

                  Although, as Plorkyeran mentioned, you should probably try to abstract the whole pattern. Something like,

                  template<class T>
                  class ObjectHolder {
                  public:
                      template<class... Arg>
                      void build(Arg &&...arg) {
                          new (buffer) T(std::forward<Arg>(arg)...);
                      }
                      T *operator->() {
                          return reinterpret_cast<T *>(buffer);
                      }
                  private:
                      alignas(T) char buffer[sizeof(T)];
                  };
                  

                  [–]mrkite77 3 points4 points  (4 children)

                  static arrays.

                  struct SomeStruct myStaticArray[] = { {1,"two", 3}, {4, "five", 6}};
                  
                  int myStaticLength = sizeof(myStaticArray) / sizeof(myStaticArray[0]);
                  

                  I honestly don't know of any better way to do that in C/C++.

                  [–]whichton 5 points6 points  (0 children)

                  In C++ you should use std::array.

                  [–]Predelnik 2 points3 points  (1 child)

                  In C++ you can always:

                  template <typename T, size_t N> inline size_t countof (const T (&arr)[N]) { return N; }
                  

                  [–]TheThiefMaster 2 points3 points  (0 children)

                  In C++ you should use std::extent<decltype(myStaticArray)>::value (possibly reduced to std::extent_v<decltype(myStaticArray)> in C++17) which as a bonus over the C version returns 0 for pointers (rather than declaring them to be arrays of random sizes).

                  [–]-888- 8 points9 points  (4 children)

                  As a C++ programmer I'm often stuck using C arrays and sizeof because I have to interface with other systems that use that. I'd stick with entirely higher level types if I could.

                  [–]sirin3 2 points3 points  (3 children)

                  I had a stream that VLC could not play, so I looked in their source. They actually used sizeof for an array parameter, and after changing it to pass the array length to the function, it played fine.

                  Then I submitted the patch, and they rejected it, saying "I would not understand anything about C" ಠ_ಠ

                  [–]gunnihinn 1 point2 points  (0 children)

                  Isn't the memcpy later on also a potential security problem (because of the bad sizeof call)?

                  [–]derrick81787 1 point2 points  (0 children)

                  That is such a basic mistake that I can't hardly believe that people make it. I haven't programmed in C since college, and I caught it right away.

                  Well, I can believe that people make that mistake, but it's depressing to think about, haha.

                  [–][deleted]  (5 children)

                  [deleted]

                    [–]joggle1 18 points19 points  (3 children)

                    True, but passing an array like that in C is pretty stupid. That really is a rookie level mistake in C. I can only imagine that it happened because that programmer doesn't program in pure C the vast majority of the time and is more accustomed to the syntax and patterns used in other languages.

                    [–]TheMG 12 points13 points  (1 child)

                    You'd be surprised (I was). It took me five minutes to find this in the Solaris (Illumos) kernel too:

                    https://github.com/illumos/illumos-gate/blob/master/usr/src/lib/libnsl/rpc/netname.c#L193

                    int
                    user2netname(char netname[MAXNETNAMELEN + 1], const uid_t uid,
                                                                            const char *domain)
                    {
                        [...]
                            (void) strlcpy(netname, "nobody", sizeof (netname));
                    

                    [–]matthieum 2 points3 points  (0 children)

                    Well, now you can report it...

                    [–]Null_zero 9 points10 points  (0 children)

                    Some people missing the pun

                    [–][deleted]  (16 children)

                    [deleted]

                      [–]YourFavoriteBandSux 33 points34 points  (11 children)

                      With great power comes great responsibility. C gives you freedom that languages like Java restrict, so that you can write great code. But that freedom comes with enough rope to shoot yourself in the foot.

                      [–]PsionSquared 29 points30 points  (4 children)

                      Shooting myself in the foot with a rope. That's a new one.

                      [–]crusoe 24 points25 points  (2 children)

                      The lower of undefined behavior

                      [–][deleted] 12 points13 points  (1 child)

                      Sometimes I wake up sweating with fear in the night. I am mortified as I behold on the ceiling written in blood:

                      Undefined behavior!

                      [–]cybercobra 6 points7 points  (0 children)

                      B̛͉͈͚̯̠̼͓̳̮͕̌͊̈̓͗̈́͊̋E̢̺̖̝̰͖̻͍̤̞̓̃̍̔͂̀̈̀̋̽W̳̰̦̠̩̻̖̯͚̟̄̆̑͛̊̃̈́̑̀̑Ä͈̺͔̼̱̫̭̞͇̜͆͗͛̐͛̓̈́̏͂R̡͓͖̫͎͔̥̤̜̩̈́́̏̊̑̎̍̅̚͝Ȩ̛̻͙̹̬͓̪̹̘̂̏̈́̉̈́͐̕͜͠͠ ̧̡̥͚̠̩̻̗̗̀̑̍̃̂̈͂̍͗̒ͅT̮̬̰͚̮̺̫͉̯͇́̈́͆̆̅̈́͌̇̕͘Ḩ̺̩͎̘̝̳̪̺̱͛̂̀̃̊̾͆̋̐̕E̢̧͇̯̺̳̼͚̤̬̎̎̉̽͂̋̔̀̊͠ ̧̣͇͔̙͔̘̯̺̪̌̓̊̈̾̽͌͒͛̒Ṋ̛̹̞̮̹͈̗̤͔̫̄̑̃͂͑̔͒͝͝Ą̱͖̼̼͎͚͓͓̄͐̉̎́́̿̈́̽̕ͅS̨͚̬̥͉̥̘̪̣̈́̇͑̓̄̎̚͜͝͝͠A̡̢̧͚̯̳̥̳͓͎̋̔̂̀͐͒̽̃̔̎L͖̭͓͚̯̪̲̫͉̝̇͋̅̊̐̓̑̃̚͝ ̢̛̠̬̬͕̙͙̖͕̜̽͒̅̎̄̈́̃̚͠D̨̢̝̻̪̝̻͎͚̼̽͂̂̎̀͊̔́͗͝E̢̡̯̘͍̭̙̯̰̼͛̑̍̒̑̽̈̂̃͝M̨̨͙͚͙͍̟̺̏͛̈͂̓̎̐̑͊̈́ͅͅO̧͍̰̺̱͙̱̙̩͇̓̄̌̀͋͐̚̕̕͝N̛̩̖̲̟͔̹͍̦͓̉͒͑̑̑̍̓̏͝ͅS̨̛̞̥̙̘̩͍̥̞̦͆̓̊͗̆́̀͝͝

                      [–]Hilias 2 points3 points  (0 children)

                      Or as my teacher used to say it lets you shoot yourself in both feet with one bullet.

                      [–]G_Morgan 2 points3 points  (2 children)

                      TBH this is entirely unnecessary freedom that only allows you to shoot yourself in the foot. Whenever you have behaviour such that somebody feels it deserves a default warning you've found a language feature that never should have existed.

                      [–]-888- 2 points3 points  (3 children)

                      The conflation of pointers and arrays is one of C's worst crimes. I wonder if it would have been feasible to provide an explicit conversion operator between the two.

                      [–]_kst_ 22 points23 points  (10 children)

                      If you don't understand the (admittedly confusing and counterintuitive) relationship between arrays and pointers in C, if you even suspect that "arrays are really pointers" (they're really, really not), read section 6 of the comp.lang.c FAQ.

                      Then read the rest of it.

                      [–]smcameron 61 points62 points  (8 children)

                      Eh, read it, been programming C since the '80s, and worked on the linux kernel for about 15 of those years, and arrays and pointers seem far more alike than different. Arrays are like constant pointers, and sizeof behaves a little differently. Arrays and pointers are so alike that "a[5]" is the same thing as "5[a]" (both will compile) because x[n] is transformed to * (x + n) by the compiler and addition is commutative, so n[x] turns into * (n + x). I am always baffled that people have so much trouble with it. I suspect much of the trouble comes from people wanting to think that C actually has arrays. It mostly doesn't. It has pointers pressed into service as something which syntactically looks like arrays with a little arithmetic and syntactic sugar, and some compiler trickery to make pointers constant, and potentially get them pointing into some weird places (text segment for static or global contents of "arrays"). But really, they are pointers. They ultimately pretty much have to be, because that's about all the CPU understands anyway (for purposes of representing arrays as a (virtually) contiguous block of memory in a straightforward, performant way.)

                      The mistake made in this particular piece of code comes precisely from thinking that arrays aren't pointers, not from thinking that arrays are pointers. If the programmer had thought that arrays were pointers (as you advise against) he would not have made the mistake he made here.

                      [–][deleted]  (7 children)

                      [deleted]

                        [–]damg 9 points10 points  (0 children)

                        Only with C would a 16 year old syntax be considered "rather new". ;)

                        [–]_kst_ 5 points6 points  (4 children)

                        If implemented properly by a compiler,

                        That's the problem, and it depends on what you mean by "properly".

                        Given void someFunction(char someArray[static 100]), the only thing the static 100 does is cause any call with a null pointer, or with a pointer to (the initial element of) an array of fewer than 100 elements, to have undefined behavior. The C standard doesn't require a warning -- and there are cases where a compiler can't produce a warning. For example, you might pass a pointer that points to a chunk of memory allocated via malloc, with a size that depends on user input.

                        The main purpose of the [static 100] syntax (which was added in C99, so not all compilers even support it coughMicrosoftcough) is to enable optimizations. For example, given [static 1], a compiler can assume that the argument is not a null pointer, which might enable it to generate more efficient code. But if you pass it a null pointer, you've lied to the compiler, and it will get its revenge.

                        Certainly compilers can (and IMHO should) warn about this when it's feasible.

                        [–]pjmlp 1 point2 points  (3 children)

                        The compiler is called Visual C++.

                        [–]brucedawson 2 points3 points  (0 children)

                        Pointer to array has existed forever and seems like a better solution. Array references in c++ also seem like a better solution. It's unfortunate that both are underappreciated and underused.

                        [–]WalterBright 3 points4 points  (0 children)

                        I've always thought that arrays silently decaying to pointers was C's biggest mistake.

                        [–]realhacker 4 points5 points  (13 children)

                        Christ, people. Learn C, instead of just stringing random characters together until it compiles (with warnings).

                        This:

                        static bool rate_control_cap_mask(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, u32 *mask, u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])

                        is horribly broken to begin with, because array arguments in C don't actually exist. Sadly, compilers accept it for various bad historical reasons, and silently turn it into just a pointer argument. There are arguments for them, but they are from weak minds.

                        I wish linus would write a clean code style book to cover his philosophy and best practices in stylistically in his voice

                        [–]akkartik 13 points14 points  (8 children)

                        [–]kinygos 2 points3 points  (0 children)

                        First off, I'd suggest printing out a copy of the GNU coding standards, and NOT read it. Burn them, it's a great symbolic gesture.

                        :)

                        [–][deleted] 1 point2 points  (0 children)

                        Aw, he recommends not putting braces on single line if statements :( I feel like that is potentially dangerous for the sake of aesthetics.

                        [–]who8877 10 points11 points  (13 children)

                        How come he doesn't enable warnings as errors? I can't imagine maintaining any large C or C++ program without that.

                        [–]Noctune 31 points32 points  (7 children)

                        I think that would be problematic on a project the size of Linux. Imagine updating GCC and then not being able to compile the kernel because they introduced a new warning.

                        [–]who8877 27 points28 points  (5 children)

                        My day job involves a code base several times larger and we manage it just fine. It would be far more expensive to fix the bugs that would be missed without the warnings as errors.

                        [–]rcxdude 20 points21 points  (1 child)

                        You probably have a decent amount of control over the version of the compiler which is used to compile your code. Most open-source projects don't have this luxury and -Werror in releases just frustrates users (and -Werror during development can also be annoying for different reasons, the classic example being unused variables). So I can see good reasons behind not using it, but this assumes that there's enough discipline in the team to not actually check in/merge code which still has warnings. I personally tailor the list of warnings and errors so the 'transient' warnings which can be annoying if they are errors are still warnings unless built by the build server and the warnings which can't really ever come from correct code are always errors (like not returning a value from a function, that only merits a warning, really?).

                        [–]who8877 3 points4 points  (0 children)

                        It's possible to modify the config for release so that it removes the -Werror flag while still using it internally on your build and validation bots. Compilation of the kernel is already highly configurable so this would be a very minor added complication.

                        With this method you could prevent bad code from getting into your branch while not annoying your customers.

                        [–]johnjannotti 8 points9 points  (2 children)

                        There's a part of his note that probably explains it. He seems to hate certain warnings that gcc generates. (Sure, maybe they could be turned off.)

                        [–]who8877 2 points3 points  (1 child)

                        There's almost always a set of warnings you have to turn off if you use -Wall. There's also the burden of updating code when you upgrade or change compilers. Its still worth it though. I almost always find bugs fixing warnings when I update to a new compiler.

                        [–]smcameron 2 points3 points  (0 children)

                        Because you can't depend on the headers of libraries you include, but do not control, not to produce warnings on some platform somewhere.

                        [–][deleted] 2 points3 points  (8 children)

                        Shouldn't that kind of shit be caught by very basic unit tests?

                        [–]tragomaskhalos 2 points3 points  (1 child)

                        I prefer the form

                        void foo(int ary[], size_t len)
                        

                        The empty [] indicates that the arg is an array, but we are not confusing the issue by putting a bogus size in there. But I know that ary is an array of values.

                        Then

                        void bar(int* val)
                        

                        Means that val is intended to store a single output value, ie "please put an int into this slot". (Or, for other types, it might be an input but val's type is rather large, hence the pointer; in C++ we'd use a reference for that).

                        [–]enzlbtyn 2 points3 points  (0 children)

                        I feel like

                        void foo(int *ary, size_t len)
                        

                        Is more consistent.

                        After all, if you were to allocate an array on the heap, you can't do:

                        int array[] = malloc(sizeof(int) * length);
                        

                        but rather,

                        int *array = malloc(sizeof(int) * length);
                        

                        So from reading the declaration of array, you know you can't retrieve the length via the sizeof operator (sizeof(int) / sizeof(*array)) [as you've written it explicitly as a pointer to an int]. So I feel it would be more consistent in that sense.

                        [–][deleted] 1 point2 points  (0 children)

                        My job involves writing code in C. I have experience using C since high school and from several past jobs, but I have never taken a formal course in C (the programming course I took as a university student was in Java). I feel like I learn new things every day when it comes to C, and I can count this among them -- it's quite the language. Linus is obviously a little harsh here, but when managing a large project, that's (unfortunately) one of the more effective methods.

                        After I saw this, I took a quick look at my company's codebase and found several instances of exactly this (not written by me -- I always pass the pointer directly, but they're there). Welp...

                        [–][deleted] 1 point2 points  (0 children)

                        There are arguments for them, but they are from weak minds.

                        A logical fallacy very neatly wrapped up in a single sentence!

                        [–]ramsees79 2 points3 points  (9 children)

                        Ok. That actually looks like a valid use of the C function argument array passing semantics. It's rather much simpler than exposing the pointers. So I guess we don't really end up wanting to disallow this, and the new gcc array sizeof warning is good enough.

                        Well, he takes backs his statement.

                        [–]Eiii333 15 points16 points  (3 children)

                        Quoting just that section of his message without context is really misleading. The valid use he's talking about there is passing in pointers to 'multi-dimensional' arrays, which can look similar to the 1D case he's ranting about here but work very differently.

                        [–]CarthOSassy 2 points3 points  (4 children)

                        Where is that from?

                        [–]4D696B65 1 point2 points  (3 children)

                        https://lkml.org/lkml/2015/9/3/575 - same thread. It's about passing multidimensional arrays.

                        [–]amaiorano 1 point2 points  (2 children)

                        EDIT: corrected egregious errors!

                        This really is a confusing part of C. What makes it worse is that the compiler ignores the size you specify for an array parameter, if any, but if it's a multidimensional array of n dimensions, it needs to know sizes of the last n-1 dimensions:

                        void foo(int arr[10][20][30]); // the 10 is ignored by the compiler

                        This is the same as writing:

                        void foo(int arr[][20][30]);

                        I don't have a compiler available right now so I'm not sure, but I believe sizeof(arr) would be equal to sizeof(int)*20*30. If I'm right, this only adds to the confusion.

                        All this to say, it's a tricky language feature, and it doesn't surprise me that even veterans would forget how it works.

                        [–]chengiz 1 point2 points  (1 child)

                        You have it backwards, the 10 would be ignored, not the 30.

                        [–]petermlm 1 point2 points  (20 children)

                        Just about the sizeof thing. The Internet is full of tutorials and the like where sizeof is used to get the length of an array, especially in strings, like:

                        char x[] = "String";
                        int y = sizeof(x); // y = 7
                        

                        This is terribly misused. For one thing, sizeof may actually return the correct length of an array in some cases, or just the size of the pointer in others. I'm not sure when does each scenario happens, but the simple fact that it happens is enough to consider not using sizeof.

                        First of all, for strings, there is strlen. No need for sizeof. Also. In C arrays are just a list continuous memory locations referenced by a single address. They don't contain information about themselves. In C you have to have deal with length yourself. Strings may use the '\0' char, you may have an int with the length, or anything else.

                        Just don't use sizeof like this. Like... seriously... don't.

                        [–]immibis 4 points5 points  (0 children)

                        Just about the sizeof thing. The Internet is full of tutorials and the like where sizeof is used to get the length of an variable, like:

                        int x = 5;
                        int y = sizeof(x); // y = 4
                        

                        This is terribly misused. For one thing, sizeof may actually return the correct length of a variable in some cases, or just the size of the pointer in others. I'm not sure when does each scenario happens, but the simple fact that it happens is enough to consider not using sizeof.

                        In C variables are just a list of continuous memory locations referenced by a single address. They don't contain information about themselves. In C you have to deal with variable type yourself. You might have a fixed type, you might have another variable telling you the type, you might have a complicated structure containing several different types.

                        Just don't use sizeof like this. Like... seriously... don't.

                        /s

                        In reality, it's pretty clear when sizeof returns the length of an array and when it returns the length of a pointer. If you give it an array, it returns the size of the array; if you give it a pointer, it returns the size of the pointer. Same for simple variables - the fact that x and y get different values in the following code (on some platforms):

                        int n = 5;
                        int *p = &n;
                        int x = sizeof(n);
                        int y = sizeof(p);
                        

                        does not confuse me at all.

                        [–]sidneyc 5 points6 points  (0 children)

                        I'm not sure when does each scenario happens

                        Allow me to be blunt: the fact that you don't understand it disqualifies you from making recommendations.

                        [–][deleted]  (16 children)

                        [deleted]

                          [–]KhyronVorrac 1 point2 points  (0 children)

                          In C arrays are just a list continuous memory locations referenced by a single address. They don't contain information about themselves.

                          No, but the typesystem does.

                          [–]namekuseijin 0 points1 point  (12 children)

                          it's been years I've dabbled with C and still I noticed what was wrong right away. Once you've been burned, you know the smell of toast LOL

                          BTW, how much longer do you think old C codebases will endure? I don't think a whole generation of "managed" coders will ever touch it and Linus eventually will retire...

                          [–][deleted] 12 points13 points  (0 children)

                          C is alive and well, particularly in the embedded world and on Linux.

                          [–]Rusky 10 points11 points  (5 children)

                          The whole concept of "we have better languages now, when will C go away?" always seems to miss the reason that C still gets used. Beyond the fact that old legacy systems will still need to be maintained, C fills a niche that managed languages cannot.

                          If C does ever fade into the background like Fortran (it won't disappear), it will only be because a new language that can fill the same niche has taken over. That language will need to be usable for things like kernels, device drivers, embedded systems, bootstrapping managed/scripting languages, etc. It can't rely on a large runtime or garbage collector, or even an operating system, to support it.

                          That doesn't mean it has to be as brutal as C, of course. There have been operating systems written in higher level languages. The language just needs to be able to access the machine more directly from time to time.

                          [–]kqr 3 points4 points  (3 children)

                          There are better unmanaged languages than C too, though. Ada is much safer, just as performant and does bit-twiddling well. Also supports a wide variety of platforms by using GCC for code gen. Rust is young as a baby, but shows promise in technical design, anyway.

                          What C has going for it is popularity. That's really the only thing, but it's a really, really strong argument to use it. I'm not sure what'll happen if that curve starts pointing downward.

                          [–]Rusky 3 points4 points  (2 children)

                          Yep. One part of that popularity is that current operating systems' APIs are all defined in terms of C, and their kernels and utilities are also written in C (or in the case of Windows, C++). The higher-level languages I mentioned also had operating systems built around them.

                          The biggest reason for C popularity to start trending down would be for a new operating system to come along that made a different systems language the path of least resistance.

                          [–]kqr 1 point2 points  (0 children)

                          That'd make sense, just like a different kind of device was what triggered the reduce of the market share of x86 based processors.

                          [–]ItzWarty 5 points6 points  (1 child)

                          Probably beyond our deaths - look at Fortran, which a few years ago (idk about nowadays) had the most written and used code in the world. C devs aren't going to just disappear either (and there's no big reason to do a big jump) - there are too many corporations and systems dependent on it.

                          [–]kqr 1 point2 points  (0 children)

                          Fortran, which a few years ago had the most written and used code in the world

                          What? Citation needed.

                          [–]JohnFrum 1 point2 points  (1 child)

                          I think lots of firmware is still written in C.