why do some writers declare and define variables separately

Discussion in 'C Programming' started by yoodavid, Sep 18, 2013.

  1. yoodavid

    yoodavid Guest

    Can anyone tell me why some writers decide to declare and
    write definitions for variables separately, rather than
    together?

    That is:
    int definelater;
    ....
    definelater = 0;

    rather than:
    int definelater = 0;

    wouldn't the later reduce code lines?
    thanks.
     
    yoodavid, Sep 18, 2013
    #1
    1. Advertisements

  2. yoodavid

    Fred K Guest

    This is really a style issue. Each person has different preferences; some come form historic use with other languages.

    You may not know what value to initialize it to. For example:
    int xxx;
    if ( something ) {
    xxx = readValueFormFile(...);
    else if ( somethingElse ) {
    xxx = getValueFromDatabase(...);
    else {
    xxx = popDialogAndGetValue(...);
    else {
    return errorValue;
    }
     
    Fred K, Sep 18, 2013
    #2
    1. Advertisements

  3. yoodavid

    Eric Sosman Guest

    Yes, and it has some other potential advantages as well.
    However, "Old Time Original C As Our Forefathers Knew It" had
    a restriction: All the declarations in a {}-enclosed block
    had to precede all the executable statements. This restriction
    was removed by the 1999 edition of the C Standard, but (even
    to this day!) some compilers have not yet caught up to 1999.
    If you're writing code that might need to work with those
    outdated compilers -- or if your habits were formed before
    the restriction was lifted -- you'll group the declarations
    at the start of each block.
     
    Eric Sosman, Sep 18, 2013
    #3
  4. Your terminology is a bit off. This line:

    int definelater;

    both *declares* and *defines* the variable. (Roughly, declaring a
    variable makes its name visible, and defining it allocate storage for
    it.) The "= 0", when it's part of the declaration is an *initializer*.
    When written separately, "definelater = 0;" is an *assignment*.

    Usually it's better, when possible, to use an initializer rather than a
    separate assignment -- not because it saves lines of code, but because
    it's clearer.

    One case where a separate assignment is more appropriate is when you
    don't know, at the point of the declaration, what value should be
    assigned:

    int obj;
    if (some_condition) {
    obj = this_value;
    }
    else {
    obj = that_value;
    }

    The ability to mix declarations and statements, introduced in C99, can
    alleviate this in some cases, making it easier to move the declaration
    to the point where you know how to initialize it:

    int x = some_value;
    int y;
    some_func(&y);
    int z = x + y;
     
    Keith Thompson, Sep 18, 2013
    #4
  5. yoodavid

    Jens Schmidt Guest

    Your code doesn't have any separate declaration. What is separate are
    definition and initialisation. A declaration would be e.g. a line
    extern int definelater;
    at the very begining.

    As I can't read the mind of "some writers", here are some guesses:
    Early versions of C didn't have the possibility to define variables
    everywhere in a function. You had to first define or declare all names
    used in the function and only then start with statements.
    Complex types, i.e. struct and union types, are not always initializeable
    inside the definition. So you have to initialize in separate statements.
    Now the programmer initialiazes everything this way for consistency.
    I'd categorize that as marginally valid at its time, but obsolete with
    modern usage.
    Reduction of code lines a non-relevant argument nowadays. The most expensive
    resource in computing is the programmer. So the criteria for coding style
    are a) is it clearly visible from the source code, what the intention was
    and b) is it improbable to introduce errors, especially later when changing
    the code.
    Actually the result is the same: initialize as soon after definition.
     
    Jens Schmidt, Sep 19, 2013
    #5
  6. A couple of minor points... If that line is at file scope, it's a
    "tentative definition" and, if nothing happens to change things, it will
    behave as if there were an initialiser (of zero).
    Technically, no. It's covered by the rules for initialising rather than
    for assigning.

    <snip>
     
    Ben Bacarisse, Sep 19, 2013
    #6
  7. yoodavid

    Eric Sosman Guest

    In modern "declare it whenever you like" C, I'd agree:
    It is almost always better to postpone defining a variable
    until you've got a reasonable initialization value. However,
    if one must define earlier I feel it is usually *not* a good
    idea to fabricate an initializer out of thin air. The common
    advice "Initialize all pointer variables to NULL" is, I think,
    a particularly bad example of bad practice.

    That's not to say it was always a bad practice. A quarter
    century ago compilers ran on sluggish systems in cramped memory
    spaces; the basic job of translating the source code took all
    the resources at their disposal and there was little left over
    for dataflow analysis. If you wrote

    char *evenOrOdd(int x) {
    char *p;
    switch (x % 2) {
    case 0: p = "even"; break;
    case 1: p = "odd"; break;
    }
    return p;
    }

    .... few old-time compilers would warn you that `p' might be
    uninitialized in the `return' statement (because `x % 2' could
    be -1). In those days, writing

    char *evenOrOdd(int x) {
    char *p = NULL;
    switch (x % 2) {
    case 0: p = "even"; break;
    case 1: p = "odd"; break;
    }
    return p;
    }

    .... could be justified because it would likely give a reproducible
    failure, easier to track down and solve than a Heisenbug.

    But that was a quarter century ago, when machines with a few
    tens of megabytes ran at a few tens of megahertz. That was back
    in the days when the `register' keyword sometimes made sense. Do
    you still use `register', or do you rely on the compiler's more
    thorough code analysis? If the latter, why try to defeat that
    same analysis when it comes to possibly uninitialized variables?

    Here's what I mean by the "defeat" remark: With today's
    compilers the first version of evenOrOdd() will almost certainly
    elicit a diagnostic, and your attention will be drawn immediately
    to the faulty code. But the second will almost certainly *not*
    draw a complaint, because the compiler can see that the variable
    *is* initialized -- to a garbage value, but the compiler doesn't
    know NULL is garbage. Instead of detecting the bug at compile
    time you detect it in testing (if you're lucky) or in deployment
    (if less than lucky) or on final approach to Mars. The safeguard
    that long ago improved the bug from "awful" to merely "bad" now
    prevents further improvement to "averted." It's a stratagem that
    refuses the compiler's offer of help -- and if you're like me,
    you shouldn't refuse help.

    Don't just initialize variables for initialization's sake.
    It's a superstition both outmoded and harmful.
     
    Eric Sosman, Sep 19, 2013
    #7
  8. yoodavid

    osmium Guest

    Amen!
     
    osmium, Sep 19, 2013
    #8
  9. yoodavid

    Les Cargill Guest

    IMO, it goes back to many architectures trapping on dereferencing a NULL
    pointer. It was a way of adding "terminating resistors" to code;
    obviating Heisenbugs.
    I would use this:

    char *p = ( ( x % 2 ) == 0 ) ? "even" : "odd" ;

    or

    const char * const mInit[] = { "even", "odd" } ;

    char *p = (char *)mInit[(x % 2)];

    The point is to have a ... violently enforced
    constraint on p that is completely invariant, no matter what.

    That's more likely to be acceptable no matter the toolchain, unless
    you're in a really old compiler.

    If you need logic more complex than that, write an initializer
    routine.

    I rather doubt that. One of the more useful things from OO is
    RAII - Resource Allocation Is Initialization. There are at least
    analogs in non-OO; this is one.

    You may well be saying the same thing and I missed it; your point is
    unclear to me.
     
    Les Cargill, Sep 19, 2013
    #9
  10. yoodavid

    Noob Guest

    And panic ensues when x % 2 equals -1?
     
    Noob, Sep 19, 2013
    #10
  11. yoodavid

    Ken Brody Guest

    I'm not sure what you are saying about optimizing, since there is nothing to
    "optimize" here. The statement "int definelater;" generates no code(*).

    (*) Okay, if "definelater" is the *only* variable in the function, then it
    make take some code to reserve space for the local variable. However, that
    same code would be required for "int definelater=0;".
     
    Ken Brody, Sep 19, 2013
    #11
  12. That last one can still have undefined behavior when x % 2 == -1.
    Following the pattern of your first example, you could write it as:

    char *p = (char *)mInit[(x % 2) == 0];

    And since p is expected to point to a string literal, it should be const
    (the same criticism applies to Eric's code upthread), which neatly
    avoids any need for casts. I might write your two examples as:

    const char *p = x % 2 == 0 ? "even" : "odd";

    and

    const char *const mInit[] = { "even", "odd" };
    const char *p = mInit[x % 2 == 0];

    (probably the former, since I find it clearer).

    [...]
     
    Keith Thompson, Sep 19, 2013
    #12
  13. yoodavid

    Eric Sosman Guest

    ... producing "odd" when x is even ...
     
    Eric Sosman, Sep 19, 2013
    #13
  14. Another argument for using the one-line version, since it makes the
    meaning clearer; the name "mInit" doesn't add any useful information
    to the reader.

    (I'm not trying either to suggest that terse code is automatically
    better or to excuse my own silly error, but it does (accidentally)
    illustrate a point.)
     
    Keith Thompson, Sep 19, 2013
    #14
  15. yoodavid

    Eric Sosman Guest

    My original point was about wanton initialization doing harm
    by suppressing a helpful diagnostic, and I wrote the function as
    a brief illustration of how suppression could occur. "Brief" and
    "buggy" were the goals; the function's, er, function was beside
    the, er, point.

    It is interesting that two count them two attempts to improve
    my silly, trivial, and intentionally buggy function have added new
    bugs in the name of clarity. This demonstrates a new point -- I'm
    not sure what the point is, exactly, but it's not a sharp one.
     
    Eric Sosman, Sep 19, 2013
    #15
  16. yoodavid

    Tim Rentsch Guest

    RAII is a C++ -ism. It has nothing to do with OOP.
     
    Tim Rentsch, Sep 22, 2013
    #16
  17. yoodavid

    Tim Rentsch Guest

    I agree with the principle but this example isn't a good
    one to illustrate it:

    int foo (int test){
    int i = test > 0 ? test : -test;
    ...
    }
     
    Tim Rentsch, Sep 22, 2013
    #17
  18. yoodavid

    Tim Rentsch Guest

    The problem is the example you gave doesn't make your point
    convincingly. To some degree it even argues against it - the
    question of whether or not a variable should be initialized may
    reasonably raise a red flag about the approach taken. And
    justifiably so I would say, since the asked-for functionality
    can be implemented without any variables at all:

    return x%2 ? "odd" : "even";
    My primary takeaway is if one to make a convincing argument about
    programming practices, the examples used should be taken from
    real-life code rather than just made up for the purpose of the
    argument.
     
    Tim Rentsch, Sep 22, 2013
    #18
  19. yoodavid

    Öö Tiib Guest

    Something it has to do. Destructors have something to do with
    OOP (being in set of OOP features of some languages). RAII has
    something to do with destructors since it is idiomatic usage of
    destructors for binding life-time of resources to scope of variables
    that encapsulate the resources.

    It may be C++ -ism that has something to do with OOP, since
    Stroustrup invented it. However it feels like useful idiom for
    Ada and for D as well. Someone using Ada or D actively could say
    better.
     
    Öö Tiib, Sep 23, 2013
    #19
  20. yoodavid

    Tim Rentsch Guest

    Object-oriented programming - both the term and the concept -
    predates not only C++-style destructors but C++ itself.
    Anyone who thinks destructors are part of OOP doesn't
    understand the term.
     
    Tim Rentsch, Sep 25, 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.