"null Considered Harmful"

Discussion in 'C Programming' started by Lynn McGuire, Dec 11, 2013.

  1. Lynn McGuire

    Lynn McGuire Guest

    "null Considered Harmful"
    http://peat.org/2013/12/07/null-considered-harmful/

    "In almost every software project I've been a part of,
    the majority of errors I've encountered are caused by
    unexpected null references. Many are caught at compile
    or test time, but they always creep though, and few
    platforms are exempt. They crop up everywhere: web
    applications, desktop applications, embedded software,
    game consoles, mobile devices -- almost everywhere there
    is software, there are null references."

    I am speechless.

    Lynn
     
    Lynn McGuire, Dec 11, 2013
    #1
    1. Advertisements

  2. Lynn McGuire

    Thomas Jahns Guest

    While I think it's every software developers business to handle error returns I
    think programming could be greatly simplified for many packages if libraries had
    a "mode" where instead of returning null/error values the program simply
    terminated (and the library defaulted to that mode). Even in the most
    sophisticated program there's errors the program is not prepared to handle.

    Fortran has this solved better than C: when I don't specify an optional error
    return argument to several built-in routines the run-time will simply abort then
    program on file errors etc. Fortran may have other problems but this makes the
    syntax of simple programs much cleaner.

    libpng does something similar via setjmp/longjmp IIRC.

    Thomas
     
    Thomas Jahns, Dec 11, 2013
    #2
    1. Advertisements

  3. Even better ist the conclusion:

    "In other words, languages that support null references are going to be
    more prone to errors, and accumulate more technical debt -- just like
    languages that support explicit memory management will be more prone to
    errors, and accumulate more technical debt."

    I propose that we deal with this pesky NULL-problem by building hardware
    that is incapable of representing NULL. Should any register at any time
    become a representation of NULL or a value out of which a representation
    of NULL might be generated the processor should hard fault.

    Best regards,
    Joe

    --
    Ah, der neueste und bis heute genialste Streich unsere großen
    Kosmologen: Die Geheim-Vorhersage.
    - Karl Kaos über Rüdiger Thomas in dsa <hidbv3$om2$>
     
    Johannes Bauer, Dec 11, 2013
    #3
  4. Baby X handles every allocation via bbx_malloc(). It's exactly the same as
    malloc(), except that it never returns null. It's not adding any functionality,
    and obviously it can't magically provide unlimited memory on demand. It's
    just to take null handling out of Baby X.

    On the other hand, Baby X makes heavy use of callbacks. Virtually every widget
    contains a callback function pointer, and an arbitrary pointer to pass back to
    it. Both of these can be null, and it's expected that in certain circumstances
    they will be null. For example you sometimes want a callback triggered
    whenever the user clicks a radio button, but more often you just want to
    query the state of the radio widget when the dialog closes.
    If we ban null, we've got to create dummy empty functions, dummy pointers
    to pass back to them. This is harder to understand, and harder to write.
     
    Malcolm McLean, Dec 11, 2013
    #4
  5. What does bbx_malloc() do if it's unable to allocate memory?
     
    Keith Thompson, Dec 11, 2013
    #5
  6. Currently it terminates the program with an error message. I'm thinking
    of providing more graceful behaviour, eg allowing an emergency shutdown
    and save.
     
    Malcolm McLean, Dec 11, 2013
    #6
  7. Lynn McGuire

    jacob navia Guest

    Le 11/12/2013 21:34, Malcolm McLean a écrit :
    Why don't you use the same stuff as in the ccl?

    You just call an error function. The user can supply one, if he cares,
    but you supply a default one that aborts the program.

    If the error function returns, and returns an integer bigger than zero
    you just go on and attempt again to allocate memory.
    If you receive an integer smaller than zero you abort.

    1) The user CAN (if he cares) try to do something in an out of memory
    condition, maybe restarting at a recovery point previously established.

    2) The user that doesn't care (or doesn't know how to fix that) will
    just use your software as it is now, and your software will have the
    same behavior.
     
    jacob navia, Dec 11, 2013
    #7
  8. LMcG> "null Considered Harmful"
    LMcG> http://peat.org/2013/12/07/null-considered-harmful/

    LMcG> "In almost every software project I've been a part of, the
    LMcG> majority of errors I've encountered are caused by unexpected
    LMcG> null references. Many are caught at compile or test time, but
    LMcG> they always creep though, and few platforms are exempt. They
    LMcG> crop up everywhere: web applications, desktop applications,
    LMcG> embedded software, game consoles, mobile devices -- almost
    LMcG> everywhere there is software, there are null references."

    LMcG> I am speechless.

    I'm not speechless. He writes like someone who's only ever worked in
    Java, and who thinks that Java is the alpha and omega of programming
    languages and environments. I see idiots like that pontificating online
    daily.

    He does have a point that, in Java, the null object is often overloaded
    to mean "no object" and "the method you just called encountered an
    error." But this is not a flaw in the concept of null; it is a flaw in
    the design of Java.

    Charlton
     
    Charlton Wilbur, Dec 11, 2013
    #8
  9. Lynn McGuire

    Ike Naar Guest

    Or remove the letters 'f' and 'o' from the alphabet.
     
    Ike Naar, Dec 11, 2013
    #9
  10. Lynn McGuire

    Eric Sosman Guest

    The cited page seems nonsense to me. But for what it's
    worth, a guy named Sir Charles Antony Richard Hoare (inventor
    of Quicksort, Turing Award recipient, Kyoto Prize recipient,
    John von Neumann Medal recipient, et cetera, et cetera) agrees
    that null is harmful.

    He also claims that he, personally, invented it.

    http://en.wikipedia.org/wiki/Tony_Hoare
     
    Eric Sosman, Dec 11, 2013
    #10
  11. Don't we have that already? If NULL is returned and this pointer
    is dereferenced (what else would it be good for?) the program
    exits.

    The real problem is returning invalid but non-NULL pointers
    since they often don't result in an immediate SIGSEGV...
    Those were a NULL pointer are involved are he relatively
    easy ones to find and fix bugs.

    The whole article cited by the OP isn't more than a rant
    by someone who can't tie a knot and thus hates shoes
    with laces. All there is is a complaint that NULL pointers
    exist but no ideas are proposed of how to eliminate them.
    Trying to ride on some famous utterance by Dijkstra just
    makes it look even more lame. And the real serious pro-
    blem of stray pointers isn't even mentioned. Well, pro-
    grammers not disciplined enough to deal with issues of
    memory allocation probably shouldn't try to use a lan-
    guage that does allow them to do so (while these more
    "protective" languages in most cases are written in a
    language like C that needs pointers to be able to write
    a language interpreter/compiler that keep less disciplined
    people from hurting themselves;-)
    That's not the Fortran I learned nearly 30 years ago;-) But this
    sounds more like a standard library issue. But then also e.g.
    strcpy() will abort the program if the source or destination
    pointer is a NULL pointer, the real problem is bad, but non-NULL
    pointers. And I have no idea how Fortran could detect them
    reliably. But then my exposure to Fortran is rather outdated,
    it was the first computer language I ever learned and I still
    shudder when remembering things like "COMMON BLOCK" and what
    atrocities could be (and in a lot of programs I've seen were)
    done with it;-).
    Regards, Jens
     
    Jens Thoms Toerring, Dec 11, 2013
    #11
  12. You hope it does, but the standard doesn't require it.
    For many protected memory systems now, the 0 address is,
    conveniently, outside your addressable space, but that
    wasn't always true.
    And many people don't check the returned values.
    Maybe one of the most forgotten is fclose().
    If an error is reported writing out the last, or
    only, buffer it will be detected at fclose() time.
    If you don't test it, the error is silently ignored.

    (snip)
    Yes, it is. The traditional (Fortran 66 days) libraries were
    pretty good at reporting errors, where C library routines
    return null and expect you to test for it. Note the above
    fclose() example.

    (More recent Fortran systems allow one to test for errors,
    or the default of the system generating a fatal message.)
    Many systems are good at detecting writes to NULL, not as many
    will notice reads. In the small model x86 days, it was usual for the
    system to not use the location at offset zero, and test at the end to
    see if it was modified. The whole 64K was in your address space, and
    besides real mode didn't do any testing.

    -- glen
     
    glen herrmannsfeldt, Dec 11, 2013
    #12
  13. And even if the system traps null pointer dereferences, an optimizing
    compiler can *assume* that the dereferenced pointer is non-null and
    make code transformations based on that assumption. I'm not sure
    I can think of a concrete example that's likely to cause problems,
    but it's something to keep in mind.
     
    Keith Thompson, Dec 12, 2013
    #13
  14. Lynn McGuire

    Eric Sosman Guest

    I think you're referring to a Linux kernel bug that got some
    play a few years back. It went something like (paraphrasing)

    void func(Thing *ptr) {
    int which = ptr->what;
    if (ptr == NULL)
    return;
    // more stuff ...

    The compiler deleted `if(ptr==NULL)return;', apparently reasoning
    that if `ptr' was NULL the preceding dereference already made the
    behavior undefined, so it didn't matter how the subsequent test came
    out. Inside the kernel, though, it turned out that the dereference
    did NOT trap: the address at NULL was mapped, and `which' received
    some garbage but legal value. And when the test was deleted, the
    Linux folks got all snooty about a "compiler error" ...
     
    Eric Sosman, Dec 12, 2013
    #14
  15. Lynn McGuire

    Nobody Guest

    Hoare is referring to the use of null values in RDBMSs and SQL. Yes, he is
    (primarily) responsible for their introduction and, yes, he now thinks
    that they're a bad idea.

    And while they are indeed problematic, so is the alternative: using a
    separate relation (table) for each column which could otherwise contain
    nulls.
     
    Nobody, Dec 12, 2013
    #15
  16. Lynn McGuire

    Nobody Guest

    FWIW, Haskell's solution is the Maybe type constructor:

    data Maybe a = Nothing | Just a

    E.g.:
    Prelude> :t lookup
    lookup :: Eq a => a -> [(a, b)] -> Maybe b
    Prelude> let x = [(1, "one"), (2, "two")]
    Prelude> lookup 1 x
    Just "one"
    Prelude> lookup 2 x
    Just "two"
    Prelude> lookup 3 x
    Nothing

    Being a distinct type, you'll get a type-mismatch error if you use a
    "Maybe T" where a "T" is expected.

    Similarly, if you use pattern-matching to extract the contained value,
    the compiler will warn you if the cases aren't exhaustive (i.e. if you
    omit the Nothing case).

    C# has Nullable<T>, but there isn't anything to prevent you from
    examining the Value property without first checking the HasValue property.

    At least Java throws a (catchable) exception for dereferencing null.
     
    Nobody, Dec 12, 2013
    #16
  17. Lynn McGuire

    Seebs Guest

    In my immediate vicinity, when we got bitten by a thing very much
    like this yielding an infinite loop, the compiler support folks we
    use pointed out what the optimization was, and the kernel people looked
    at it and said "oh, well, the compiler's right, then, let's fix the
    code."

    -s
     
    Seebs, Dec 12, 2013
    #17
  18. Lynn McGuire

    Thomas Jahns Guest

    I haven't looked at your code and what bbx_malloc tests for. You are aware that
    NULL is a valid return value for an equally valid malloc(0)?

    Thomas
     
    Thomas Jahns, Dec 12, 2013
    #18
  19. I think better solution is to use exceptions.
    You can't ignore an exception, whilst return code is very easy to ignore.
     
    Павел Дмитриев, Dec 12, 2013
    #19
  20. Here's the code.

    void *bbx_malloc(int size)
    {
    void *answer;

    if(size == 0)
    size = 1;
    answer = malloc(size);
    if(!answer)
    {
    fprintf(stderr, "Out of memory\n");
    exit(EXIT_FAILURE);
    }
    return answer;
    }

    So we return a one byte allocation on a request for zero bytes.

    It's something and nothing. Also it takes an int, preventing anyone from
    creating a heap array that cannot be indexed by plain int, so Baby X doesn't
    need to mess about with size_t index variables.
     
    Malcolm McLean, Dec 12, 2013
    #20
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.