BSD's Shutdown Module



  • This is the code in BSD's shutdown.c. Scroll down below the #undef's, past the static variables, and check the signature of the second function from top to bottom.

    I guess there was some level of trial-and-error-and-frustration in writing that one.



  • For those lazier than me:

    		/*
    * check if the specified year is in the next century.
    * allow for one year of user error as many people will
    * enter n - 1 at the start of year n.
    */


  • That, too (nice find).



  • whoops

    die_you_gravy_sucking_pig_dog()






  • Discourse touched me in a no-no place

    @Mole said:

    http://www.codinghorror.com/blog/archives/001211.html
    Hmm - taking their site name literally this time I see.

    From the page mentioned - and in the first paragraph no less:

    In the C programming language, you're regularly forced to deal with the painful, dangerous concepts of pointers and explicit memory allocation.

    [code]b1 = (double *)malloc(m*sizeof(double));[/code]

    What's that cast doing there? <font color="#ffffff"><stdlib.h> makes it redundant</font>

    Why are they using <font face="courier new,courier">sizeof(double)</font> instead of <font face="courier new,courier">sizeof *b1</font>? <font color="#ffffff">What happens if b1 is declared float?</font>

     

     



  • I take it you're not at all familiar with pointers and manual memory management.

    @PJH said:

    What's that cast doing there? <font color="#ffffff"><stdlib.h> makes it redundant</font>

     malloc returns a void*.


    Why are they using <font face="courier new,courier">sizeof(double)</font> instead of <font face="courier new,courier">sizeof *b1</font>? <font color="#ffffff">What happens if b1 is declared float?</font>

     

    Um, because *b1 is an undefined memory location? This is the [b]whole point of malloc[/b]: to allocate a block of memory at a given size, and pass you the location of said memory. How else are you going to tell it what size you want if you don't specify the size of the TYPE you want (in this case, sizeof(double) ) ?

     I highly recommend a basic C programming book. Go read about and learn how memory allocation and pointers work.



  • @PJH said:

    In the C programming language, you're regularly forced to deal with the painful, dangerous concepts of pointers and explicit memory allocation.

    <font size="2" face="Lucida Console">b1 = (double *)malloc(m*sizeof(double));</font>

    What's that cast doing there? <font color="#ffffff"><stdlib.h> makes it redundant</font>

     

    I don't know about other compilers but when gcc is working in c++ mode it won't allow different pointer types to be assigned to each other (like double *d; float *f=d;) so you have to cast it explicitly. I tested this and it's true. I think the microsoft compiler should act the same way because AFAIK it's an ISO standard.

     Also you should probably use sizeof(*p) instead of the type but as long as you know what you're doing it's not that big a deal.

    Anyway, they just gave a example, the same one most would use when quickly mashing up a little program.



  • @JamesKilton said:

    I take it you're not at all familiar with pointers and manual memory management.

    .... snipping some BS

     I highly recommend a basic C programming book. Go read about and learn how memory allocation and pointers work.

     

    ... says the guy who doesn't have a clue.

    First, in C (not C++) casts between void* and any pointer type are implicit. int* p = malloc(32); will compile just fine on any compiler (in C mode, mind you)

    Second, sizeof is evaluated during compilation (yes, there're exceptions, but that's irrelevant to this discussion) so int* p = malloc(5*sizeof(*p)) is also perfectly fine. The compiler only cares about the type of *p which is known at compile time and whether p is allocated, 0 or 0xdeadbeef at runtime couldn't matter less.

    And please, don't involve Jeff Attwood when discussing techincal matters, I'm actually glad that he spends his time blogging instead of coding (and at the same time horrified by the fact that there are probably beginners who take his words seriously)


  • Discourse touched me in a no-no place

    @hvm said:

    I don't know about other compilers but when gcc is working in c++ mode
    This isn't C++ though. It's C. There's a few subtle differences. One of which resulted in my question.


  • Discourse touched me in a no-no place

    @JamesKilton said:

    I take it you're not at all familiar with pointers and manual memory management.
    I'm very familiar thank you very much. Which is why I asked those two questions.@JamesKilton said:

    @PJH said:

    What's that cast doing there?<font color="#000000"> </font><font color="#000000"><stdlib.h> makes it redundant</font>

     malloc returns a void*.

    Indeed it does. And C can convert void* to any other pointer type, and back again. So the cast is not only redundant, it's dangerous since your compiler fails to warn you that you haven't included <stdlib.h> (as I indicated.)

    @JamesKilton said:


    Why are they using <font face="courier new,courier">sizeof(double)</font> instead of <font face="courier new,courier">sizeof *b1</font>?<font color="#000000"> </font><font color="#000000">What happens if b1 is declared float?</font>

     

    Um, because *b1 is an undefined memory location?

    Irrelevant.. You're taking the size of what it's pointing to, which is a compile time operation, not a run-time operation which you appear to think.)

    @JamesKilton said:

    How else are you going to tell it what size you want if you don't specify the size of the TYPE you want

    Erm - that's exactly what <font face="courier new,courier">sizeof *b</font> does. And is more forgiving of type changes as I indicated earlier.

     @JamesKilton said:

    I highly recommend a basic C programming book. Go read about and learn how memory allocation and pointers work.

     I too highly recommend a basic C programming book. Not for me though.



  • So the cast is not only redundant, it's dangerous since your compiler fails to warn you that you haven't included <stdlib.h> (as I indicated.)

    How on earth would a cast to (double*) cause the compiler to not warn you about a missing header? (For the record, gcc does give a warning, with or without the cast.)



  •  I would expect an 'implicit declaration of malloc' error mesage.



  • heron@taim ~ $ cat /tmp/temp.c
    int main()
    {
            double* b = (double*)malloc(sizeof(double));
            free(b);

            return 0;
    }

    heron@taim ~ $ gcc /tmp/temp.c
    /tmp/temp.c: In function 'main':
    /tmp/temp.c:3: warning: incompatible implicit declaration of built-in function 'malloc'



  • Depends on the version of gcc. Older ones weren't too eager to warn.



  • @Heron said:

    heron@taim ~ $ cat /tmp/temp.c
    int main()
    {
            double* b = (double*)malloc(sizeof(double));
            free(b);

            return 0;
    }

    heron@taim ~ $ gcc /tmp/temp.c
    /tmp/temp.c: In function 'main':
    /tmp/temp.c:3: warning: incompatible implicit declaration of built-in function 'malloc'

    Boy oh boy are you screwed on any m68k or LP64 platform!


  • Aside from the odd function name, I feel I need to ask something: What's with all the casts to void? As in
    (void)pclose(pf);
    I've never written anything in C (only C++), but it looks to me to be a useless construct..



  • @PJH said:

    What's that cast doing there? <stdlib.h> makes it redundant

    The cast is making the code look more complicated, but otherwise harming no-one. Don't worry about it.
    @PJH said:
    Why are they using sizeof(double) instead of sizeof b1? What happens if b1 is declared float?

    It wouldn't compile on a sensible C compiler, whatever you did with sizeof. You would need to put something like "b1 = (int)malloc(m
    sizeof(double));", and you would ideally have no real interest in using the allocated memory for anything at all.



  • @AltSysrq said:

    Aside from the odd function name, I feel I need to ask something: What's with all the casts to void? As in
    (void)pclose(pf);
    I've never written anything in C (only C++), but it looks to me to be a useless construct..

    It suppresses a Lint warning IIRC.  Some compilers might warn about it too at higher levels of strictness, though I couldn't find an example off the top of my head.

     



  • @_moz said:

    @PJH said:
    What's that cast doing there? <stdlib.h> makes it redundant

    The cast is making the code look more complicated, but otherwise harming no-one. Don't worry about it.

    It can be harmful if you haven't got the include though.

    @_moz said:

    @PJH said:
    Why are they using sizeof(double) instead of sizeof *b1? What happens if b1 is declared float?

    It wouldn't compile on a sensible C compiler, whatever you did with sizeof. You would need to put something like "b1 = (int)malloc(m*sizeof(double));", and you would ideally have no real interest in using the allocated memory for anything at all.
    I believe PJH really meant "what if b1 is declared pointer to float?", since that's the obvious gist of the question.  Or maybe that's why that last sentence was posted in white text on a white background. ... wtf?  I didn't even know it was there until you quoted it!

    @PJH said:

    What's that cast doing there? <font color="#ffffff">&lt;stdlib.h&gt; makes it redundant</font><br></p>
    <p>Why are they using <font face="courier new,courier">sizeof(double)</font> instead of <font face="courier new,courier">sizeof *b1</font>? 
    <font color="#ffffff">What happens if b1 is declared float?</font> </p><p>&nbsp;</p><p>&nbsp;</p>

    Is this supposed to be Invisiclues week or something?



  • @DaveK said:

    Boy oh boy are you screwed on any m68k or LP64 platform!
     

    Would you mind elaborating?



  • @Heron said:

    @DaveK said:

    Boy oh boy are you screwed on any m68k or LP64 platform!
     

    Would you mind elaborating?

    The problem with m68k or any LP64 is that sizeof pointer != sizeof int.

    I don't see what's wrong with your code, though. You aren't casting any ints as pointers or vise-versa. You're just casting one kind of pointer as another.



  •  Yeah, that code sample doesn't make pointer size assumptions...



  • @Heron said:

    So the cast is not only redundant, it's dangerous since your compiler fails to warn you that you haven't included <stdlib.h> (as I indicated.)

    How on earth would a cast to (double*) cause the compiler to not warn you about a missing header? (For the record, gcc does give a warning, with or without the cast.)

    The C language allows implicit declarations of functions.  The return type of such a declaration is int.  I'm not sure of parameters, but they're irrelevant for this problem.  Thus, missing <stdlib.h> will result in an implicit declaration of int malloc().  Now, assigning an int to a pointer is an error, so compilation would fail without the cast.  But!  By doing the typecast, you're essentially telling the compiler to STFU, and so it does, accepting your code.  GCC indeed does still give a warning, but it's only a warning, and can be turned off or ignored.

    This would also be the reason for being screwed up on m68k or LP64, I believe.



  •  @AltSysrq said:

    Aside from the odd function name, I feel I need to ask something: What's with all the casts to void? As in
    (void)pclose(pf);

    pclose() returns an int. The code does nothing with the return value, so that piece of code is telling the compiler (and any source code checkers) that they know it returns something, but they are not interested in what it returns. 



  • @tdb said:

    The C language allows implicit declarations of functions.  The return type of such a declaration is int.  I'm not sure of parameters, but they're irrelevant for this problem.  Thus, missing <stdlib.h> will result in an implicit declaration of int malloc().  Now, assigning an int to a pointer is an error, so compilation would fail without the cast.  But!  By doing the typecast, you're essentially telling the compiler to STFU, and so it does, accepting your code.  GCC indeed does still give a warning, but it's only a warning, and can be turned off or ignored.

    This would also be the reason for being screwed up on m68k or LP64, I believe.

     

    Ah, but it does give a warning - thus notifying the programmer that there is a problem (even if that problem compiles into something resembling valid assembly).  Any programmer worth his or her salt will fix the cause of the warning...

     (edited to add:)

    I didn't even compile with -Wall or -Werror, which are fairly standard practice among programmers using gcc (including myself).  People who use -Wall don't turn off warnings; that would defeat the purpose of -Wall.

    Also, it's not the typecast that makes the compiler accept the implicitly declared function; if you compile the same code without the cast, it will still compile, and you'll get the exact same warning.


  • Discourse touched me in a no-no place

    @Heron said:

    How on earth would a cast to (double*) cause the compiler to not warn you about a missing header?

    As already mentioned, without that header, malloc() effectively returns an int, not void*, and since C doesn't automatically convert ints to pointers, any compiler should complain about it.

    @Heron said:

    (For the record, gcc does give a warning, with or without the cast.)
    It shouldn't warn if you put the cast in there.The cast is your way of saying to the compiler 'I know what I'm doing - stop complaining.'



  • @PJH said:

    It shouldn't warn if you put the cast in there.The cast is your way of saying to the compiler 'I know what I'm doing - stop complaining.'
     

    The warning seems to be because the implicit "int malloc()" conflicts with the actual "void* malloc()" built in to GCC's runtime.

    Implicit function definitions are evil anyway.



  • @PJH said:

    @Heron said:

    (For the record, gcc does give a warning, with or without the cast.)
    It shouldn't warn if you put the cast in there.The cast is your way of saying to the compiler 'I know what I'm doing - stop complaining.'

    It should complain regardless, as even though you are casting the return type, the compiler still doesn't know anything about what the called routine is expecting in arguments. It will assume int, but the function itself could be expecting a int64, char, 64-bit double or anything else. Passing an 32-bit int on the stack when the called function extracts 64-bits of data is a brilliant way of trashing the stack and crashing the program due to the return address being mashed (assuming that arguments are passed onto the stack and the callee doesn't fix the stack after the call, of course).

    In this case the above is unlikely to happen, as a single parameter is more likely to be passed in a processor register, but we are not just taking about malloc() here, but things in general.



  • I like to abuse malloc() as follows, which GC languages will probably not allow:

    typedef struct structA_
    {
       //stuff
    }structA;
    

    typedef struct structB_
    {
    structA* reference;
    // other stuff
    }structB;

    int some_function()
    {
    structB* newB;

    // need to allocate a full structB, including its reference:
    // but allocate it all as one block so we only have to check one allocation:
    
    if( (newB = (structB*)malloc(sizeof(structB)+sizeof(structA)) ) )    
    {
        newB-&gt;reference = (structA*)&amp;newB[1];    // maintainers hate me!
    }
    

    }



  • @Heron said:

    Also, it's not the typecast that makes the compiler accept the implicitly declared function; if you compile the same code without the cast, it will still compile, and you'll get the exact same warning.

    I tried with gcc-3.3, it did warn without the cast, but not with the cast.
    Probably they've raised the default warning level for good reasons.



  • @Heron said:

    @DaveK said:

    Boy oh boy are you screwed on any m68k or LP64 platform!
     

    Would you mind elaborating?

    In the example, you have implicitly defined malloc as returning an int.

    On an m68k platform, the ABI says that scalar values are passed and returned in data regs, and pointer values passed and returned in address regs.

    So malloc will return a pointer to the allocated memory in a0, but the compiler will think it's in d0, and cast that (uninitialised) value to a pointer.

    On an LP64 platform, ints only have 32 bits, pointers have 64.  As soon as you cast it from int to pointer, you'll truncate off the upper 32-bits, even if they did get returned in the same register.

    Also, demons will fly out of your nose.

     

    :-) 



  • @too_many_usernames said:

    I like to abuse malloc() as follows, which GC languages will probably not allow:

    Well, GC languages don't let you malloc blocks of memory, know the size of objects, or create objects from arbitrary memory blocks, so it's a non-issue really.

    In C it's ok, but why on earth not just define a 'derived' class:

    @in interface (header file) said:

    typedef struct structA_
    {
    //stuff
    }structA;

    typedef struct structB_
    {
    structA* reference;
    // other stuff
    }structB;

    @in implementation .C file said:

    typedef struct structB_with_embeddedA
    {
    structB b;
    structA
    }structB_with_embeddedA;

    int some_function()
    {
        structB_with_embeddedA *newob;
        structB *newB;
        // need to allocate a full structB, including its reference:
    // but allocate it all as one block so we only have to check one allocation:

    if( (newob = malloc(sizeof(structB_with_embeddedA)) ) )
    {
     	newB = &newob->b;
            newB->reference = &newob->a;    // maintainers love me!
    }
    }
    Your real problem is knowing whether to free() any given struct A pointer, since it might or might not be part of a struct B.


  • @DaveK said:


    @in implementation .C file said:

    typedef struct structB_with_embeddedA
    {
    structB b;
    structA
    }structB_with_embeddedA;

    int some_function()
    {
        structB_with_embeddedA *newob;
        structB *newB;
        // need to allocate a full structB, including its reference:
    // but allocate it all as one block so we only have to check one allocation:

    if( (newob = malloc(sizeof(structB_with_embeddedA)) ) )
    {
     	newB = &newob->b;
            newB->reference = &newob->a;    // maintainers love me!
    }
    }

    Yes, but what if structB is a container class?  This way you don't necessarily know the type of structA...so the reference is really a void*.  (Yes, you may ask "why not use C++ or something that actually has built-in class support?" and have a reasonable question. You will likely not get a reasonable answer though.)

    @DaveK said:

    Your real problem is knowing whether to free() any given struct A pointer, since it might or might not be part of a struct B.
     

    Ah, but half the fun is making sure you code such that you never try to free arbitrary structA pointers. =)

    (Incidentally, your suggestion would still choke if you tried <font face="courier">free(newB->reference)</font> since you'd be trying to free something in the middle of an allocated area, so the fundamental problem is not solved.)



  • Well good thing GCC warns me about the implicit function definition, so I would realize the mistake and correct it - and there would be no problems.  So why would I be screwed again?  The compiler does what it's supposed to - it warns me there may be a problem, even though it's valid code.

    In any case, the point is largely academic - I'm obsessive about including the right headers, so it's not a situation that's likely to arise.



  • @Heron said:

    Well good thing GCC warns me about the implicit function definition, so I would realize the mistake and correct it - and there would be no problems.  So why would I be screwed again?  The compiler does what it's supposed to - it warns me there may be a problem, even though it's valid code.

    In any case, the point is largely academic - I'm obsessive about including the right headers, so it's not a situation that's likely to arise.

    Blimey, don't take it personally.  We were discussing the piece of code you posted, not some hypothetical other piece of code that would be how you would actually do differently in order to get it correct!  Would it have been clearer if I had said

    @DaveK said:

    Boy oh boy is one screwed on any m68k or LP64 platform!
    ... to make clear I was talking about the "generic you" abstract person?!

     



  • @too_many_usernames said:

    @DaveK said:

    Your real problem is knowing whether to free() any given struct A pointer, since it might or might not be part of a struct B.
     

    Ah, but half the fun is making sure you code such that you never try to free arbitrary structA pointers. =)

    (Incidentally, your suggestion would still choke if you tried <font face="courier">free(newB->reference)</font> since you'd be trying to free something in the middle of an allocated area, so the fundamental problem is not solved.)

    Errr, I believe I said that.  The <font face="courier">(newB->reference)</font> pointers are very much what I would consider "any given struct A pointer", I wasn't artificially restricting my meaning to only count scalar pointer variables.



  • @DaveK said:

    Errr, I believe I said that.  The <font face="courier">(newB->reference)</font> pointers are very much what I would consider "any given struct A pointer", I wasn't artificially restricting my meaning to only count scalar pointer variables.

     

    Re-reading DaveK's original post again....
    :
    :
    Bah, yeah, that is exactly what you said.  I have no idea what made me think you were saying something else there.


Log in to reply