Good C programming style

Discussion in 'C Programming' started by Sensei, Oct 7, 2005.

  1. Sensei

    Sensei Guest

    Hi!

    I'm thinking about a good programming style, pros and cons of some
    topics. Of course, this has nothing to do with indentation... Students
    are now java-dependent (too bad) and I need some serious motivations
    for many issues... I hope you can help me :) I begin with the two major
    for now, others will come for sure!

    - function variables: they're used to java, so no pointers. Personally
    I'd use always pointers, but why could be better to use:
    void myfun(int &i) or (int *i) or (int i)
    given that the function can and cannot (the last case of course) modify
    a parameter. I see performance issues (stack related)...

    - defines: why they use function and variable defines? why they shouldn't?
    #define MY_PI 3.1415
    #define fun(x) ((x) * (x))


    --
    Sensei <>

    The difference between stupidity and genius is that genius has its
    limits. (A. Einstein)
    Sensei, Oct 7, 2005
    #1
    1. Advertising

  2. Sensei

    Mike Wahler Guest

    "Sensei" <> wrote in message
    news:di6gc0$m52$...
    > Hi!
    >
    > I'm thinking about a good programming style, pros and cons of some topics.
    > Of course, this has nothing to do with indentation... Students are now
    > java-dependent (too bad) and I need some serious motivations for many
    > issues...


    So you're teaching C?

    > I hope you can help me :) I begin with the two major for now, others will
    > come for sure!
    >
    > - function variables: they're used to java, so no pointers. Personally I'd
    > use always pointers, but why could be better to use:
    > void myfun(int &i)


    but you don't appear able to distinguish C from C++

    > or (int *i) or (int i)
    > given that the function can and cannot (the last case of course) modify a
    > parameter. I see performance issues (stack related)...


    and you don't appear to be aware of the consensus about
    premature optimization

    >
    > - defines: why they use function and variable defines? why they shouldn't?
    > #define MY_PI 3.1415
    > #define fun(x) ((x) * (x))


    and you don't appear to be aware of the type-checking that a
    real function can give, and that macros do not.

    >



    Running quickly in the other direction,
    -Mike
    Mike Wahler, Oct 7, 2005
    #2
    1. Advertising

  3. Sensei

    Skarmander Guest

    Sensei wrote:
    > I'm thinking about a good programming style, pros and cons of some
    > topics. Of course, this has nothing to do with indentation... Students
    > are now java-dependent (too bad) and I need some serious motivations for
    > many issues... I hope you can help me :) I begin with the two major for
    > now, others will come for sure!
    >

    The questions you're asking are elemental. Read a good book on C and
    these topics should be treated in some detail, along with explanations
    that will help you decide on what is appropriate and why.

    > - function variables: they're used to java, so no pointers. Personally
    > I'd use always pointers, but why could be better to use:
    > void myfun(int &i) or (int *i) or (int i)


    int &i is not C. You may be thinking of C++.

    As for when to use pointers and when not, it's simple: pointers for
    variable-sized data like strings, pointers also to implement
    call-by-reference in C, that is, arguments you'll need to change.
    Pointers also for structs, see below. And finally, pointers for
    functions, since there's no other way to pass them.

    > given that the function can and cannot (the last case of course) modify
    > a parameter. I see performance issues (stack related)...
    >

    Mostly irrelevant. A C idiom is to always pass structure types by
    pointer because passing most structures by value means copying, which is
    indeed a waste of time in most cases, especially if the value isn't
    modified. While a compiler could optimize this in some cases, it's not
    expected to, and programmers don't count on it.

    > - defines: why they use function and variable defines? why they shouldn't?
    > #define MY_PI 3.1415


    Established C practice is to use #define for constants since C is a bit
    puzzling when it comes to constants. Declaring a variable "const" does
    not make its value a constant expression for purposes of array sizes,
    for example. Also, memory is allocated for const variables (great
    oxymoron, incidentally :) when it typically isn't necessary.

    There's no such thing as a "function" or "variable" define, however.
    Macros just establish textual replacement rules.

    > #define fun(x) ((x) * (x))
    >

    Use functions when you can, macros when you have to. The example above
    is a case of when you don't have to.

    As for when you need macros, that's not a matter of coding style.

    S.
    Skarmander, Oct 7, 2005
    #3
  4. Sensei <> writes:
    > I'm thinking about a good programming style, pros and cons of some
    > topics. Of course, this has nothing to do with indentation... Students
    > are now java-dependent (too bad) and I need some serious motivations
    > for many issues... I hope you can help me :) I begin with the two
    > major for now, others will come for sure!
    >
    > - function variables: they're used to java, so no pointers. Personally
    > I'd use always pointers, but why could be better to use:
    > void myfun(int &i) or (int *i) or (int i)
    > given that the function can and cannot (the last case of course)
    > modify a parameter. I see performance issues (stack related)...


    The term "function variables" is misleading; you mean parameters (the
    things declared between the parentheses in a function declaration) or
    arguments (the actual values passed in a function call).

    The declaration "void myfun(int &i)" is not valid C. (I think it's
    C++; see comp.lang.c++.)

    The choice between "void myfun(int *p)" and "void myfun(int i)" isn't
    really a matter of style; it's just a matter of what the function
    needs to do. (Note that I've changed the name of the pointer
    parameter; calling a pointer "i" is misleading.) If myfun needs to
    change the value of an object specified by the caller, or if you want
    to point to an array of integers, use a pointer. If you just want to
    pass an integer value, use an int.

    Note that if myfun's parameter is a pointer, you can only pass the
    address of an ojbect (or a null pointer); you can't pass 42 or x+3.

    If the parameter is of a struct type, it often makes sense to pass a
    pointer even if you don't want to modify the struct object. Passing
    an argument means making a copy of the argument and storing it in the
    parameter; for large structures, this can be significantly expensive.
    This consideration doesn't apply to ints, since there's no significant
    performance difference between passing a copy of an int and passing a
    copy of its address.

    > - defines: why they use function and variable defines? why they shouldn't?
    > #define MY_PI 3.1415
    > #define fun(x) ((x) * (x))


    For MY_PI (BTW, everybody else's PI is closer to 3.1416), C doesn't
    provide a mechanism for defining a constant floating-point value. You
    can declare
    const double my_pi = 3.14159265358979323848264;
    but that creates a write-only variable, not a real constant. Macros
    are useful for this kind of thing.

    As for function-like macros, they're sometimes the best way to create
    inline functions. (C99 has inline functions, but not all compilers
    support them.) There are also things you can do in a macro that you
    can't easily do in a function, such as using a type name as an
    argument. But try not to be too tricky. Macros can be dangerous,
    since they're expanded in a very early stage of compilation. They can
    *look* like function calls or variable declarations, but they don't
    necessarily act like them; for example, they pay no attention to
    scope.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
    Keith Thompson, Oct 7, 2005
    #4
  5. Sensei

    Skarmander Guest

    Keith Thompson wrote:
    <snip>
    > You can declare
    > const double my_pi = 3.14159265358979323848264;
    > but that creates a write-only variable, not a real constant. Macros
    > are useful for this kind of thing.
    >

    You know, a write-only variable is about the only thing C's missing. :-D

    S.
    Skarmander, Oct 7, 2005
    #5
  6. Sensei

    Flash Gordon Guest

    Sensei wrote:
    > Hi!
    >
    > I'm thinking about a good programming style, pros and cons of some
    > topics. Of course, this has nothing to do with indentation... Students
    > are now java-dependent (too bad) and I need some serious motivations for
    > many issues... I hope you can help me :) I begin with the two major for
    > now, others will come for sure!
    >
    > - function variables: they're used to java, so no pointers. Personally
    > I'd use always pointers, but why could be better to use:
    > void myfun(int &i) or (int *i) or (int i)


    void myfun(int &i) is an error in C. The two options that are C that you
    list are
    void myfun(int *i)
    void myfun(int i)

    > given that the function can and cannot (the last case of course) modify
    > a parameter. I see performance issues (stack related)...


    C does not require a stack, but assuming a stack implementation in
    either case something will be pushed on to it, either the value of an
    int or a pointer to an int. Do you *really* think either will have a
    significant performance difference?

    Write what you mean, don't try to micro-optimise.

    > - defines: why they use function and variable defines? why they shouldn't?
    > #define MY_PI 3.1415


    How else would you define MY_PI? Apart from more accurately, that is ;-)

    > #define fun(x) ((x) * (x))


    In this instance fun will work on any arithmetic type which can
    sometimes be an advantage. I would generally recommend using functions
    rather than function like macros *except* where you have a specific
    reason to need a function like macro (e.g. if you wanted fun above to
    operate on any arithmetic type and not have it convert everything to
    double or long double).

    Macro's can have unexpected effects such as given the above definition
    if you do
    fun(a++)
    you actually invoke undefined behaviour because it will expand to
    ((a++) * (a++))
    which attempts to modify a twice without an intervening sequence point.
    This is why you should not use function like macros except where you
    need them. It is also why you should follow the convention of spelling
    macros in UPPER CASE to give people an immediate warning.
    --
    Flash Gordon
    Living in interesting times.
    Although my email address says spam, it is real and I read it.
    Flash Gordon, Oct 7, 2005
    #6
  7. Sensei

    Joe Wright Guest

    Skarmander wrote:
    > Keith Thompson wrote:
    > <snip>
    >
    >> You can declare
    >> const double my_pi = 3.14159265358979323848264;
    >> but that creates a write-only variable, not a real constant. Macros
    >> are useful for this kind of thing.
    >>

    > You know, a write-only variable is about the only thing C's missing. :-D
    >
    > S.


    The write-only characteristic is priceless. In a former life I designed
    a ram-disk subsystem, memory which should 'look' like a disk drive.
    Until I got it working, I referred to it as write-only memory.

    --
    Joe Wright
    "Everything should be made as simple as possible, but not simpler."
    --- Albert Einstein ---
    Joe Wright, Oct 7, 2005
    #7
  8. Sensei wrote:
    > Hi!
    >
    > I'm thinking about a good programming style, pros and cons of some
    > topics. Of course, this has nothing to do with indentation... Students
    > are now java-dependent (too bad) and I need some serious motivations
    > for many issues... I hope you can help me :) I begin with the two major
    > for now, others will come for sure!
    >
    > - function variables: they're used to java, so no pointers. Personally
    > I'd use always pointers, but why could be better to use:
    > void myfun(int &i) or (int *i) or (int i)


    int &i is not standard C.

    > given that the function can and cannot (the last case of course) modify
    > a parameter. I see performance issues (stack related)...
    >
    > - defines: why they use function and variable defines? why they shouldn't?
    > #define MY_PI 3.1415


    You might want to extend pi a few more decimal places. The SUSv3
    standard has a macro M_PI in <math.h>, but it isn't ISO-99 C.

    > #define fun(x) ((x) * (x))


    This is considered bad style for a macro because the macro argument is
    evaluated twice. Imagine what would happen if the argument to the macro
    contained an increment or decrement operator.

    Gregory Pietsch

    >
    >
    > --
    > Sensei <>
    >
    > The difference between stupidity and genius is that genius has its
    > limits. (A. Einstein)
    Gregory Pietsch, Oct 7, 2005
    #8
  9. Sensei

    Skarmander Guest

    Joe Wright wrote:
    > Skarmander wrote:
    >
    >> Keith Thompson wrote:
    >> <snip>
    >>
    >>> You can declare
    >>> const double my_pi = 3.14159265358979323848264;
    >>> but that creates a write-only variable, not a real constant. Macros
    >>> are useful for this kind of thing.
    >>>

    >> You know, a write-only variable is about the only thing C's missing. :-D
    >>

    >
    > The write-only characteristic is priceless. In a former life I designed
    > a ram-disk subsystem, memory which should 'look' like a disk drive.
    > Until I got it working, I referred to it as write-only memory.
    >

    I have actually worked on machines that had write-only memory, typically
    memory-mapped hardware (the addresses corresponding to "out" ports of
    some sort, of course). Aside from the curiousity value, it's really not
    that different from regular memory -- you won't use it to store
    temporaries or read back what you've written, after all.

    S.
    Skarmander, Oct 7, 2005
    #9
  10. Sensei

    pete Guest

    Keith Thompson wrote:

    > const double my_pi = 3.14159265358979323848264;


    Where the hell did you get the idea that the next digit after
    3.1415926535897932384 was 8?!!!

    ;)

    --
    pete
    pete, Oct 8, 2005
    #10
  11. Sensei

    Sensei Guest

    On 2005-10-07 21:28:50 +0200, "Mike Wahler" <> said:

    > "Sensei" <> wrote in message
    > news:di6gc0$m52$...
    >> Hi!
    >>
    >> I'm thinking about a good programming style, pros and cons of some
    >> topics. Of course, this has nothing to do with indentation... Students
    >> are now java-dependent (too bad) and I need some serious motivations
    >> for many issues...

    >
    > So you're teaching C?



    Nope. I'm an engineer working on theoretics. I *have* to teach C, and
    I'm studying before doing anything wrong.


    > but you don't appear able to distinguish C from C++



    Correct. I'm trying.


    >
    > and you don't appear to be aware of the consensus about
    > premature optimization



    My biggest programming efforts have always been on assembly. I'm used
    to do a shift instead of dividing or multiplying by 2. I've read it's
    not useful on high level languages like C.

    If a variable is passed on the stack, I imagine *i pushes an address,
    while i pushed the entire data, so if i is a big structure is not a
    nice thing.

    Moreover, I don't know if C says *how* parameters are passed to
    functions: stack, pointer, whatever...


    >
    >>
    >> - defines: why they use function and variable defines? why they shouldn't?
    >> #define MY_PI 3.1415
    >> #define fun(x) ((x) * (x))

    >
    > and you don't appear to be aware of the type-checking that a
    > real function can give, and that macros do not.



    Of course macros are macros. No checks, no functions. Why using one
    instead of another is not clear to me. As I said, I'm an assembly guy,
    and ``functions'' do not really exist.

    You don't appear to understand that people could ask for informations
    here about std-c with different knowledge :)


    --
    Sensei <>

    The difference between stupidity and genius is that genius has its
    limits. (A. Einstein)
    Sensei, Oct 8, 2005
    #11
  12. Sensei

    Sensei Guest

    On 2005-10-07 21:35:26 +0200, Skarmander <> said:

    > Sensei wrote:
    >> I'm thinking about a good programming style, pros and cons of some
    >> topics. Of course, this has nothing to do with indentation... Students
    >> are now java-dependent (too bad) and I need some serious motivations
    >> for many issues... I hope you can help me :) I begin with the two major
    >> for now, others will come for sure!
    >>

    > The questions you're asking are elemental. Read a good book on C and
    > these topics should be treated in some detail, along with explanations
    > that will help you decide on what is appropriate and why.



    As I said in another post, I'm studying now.

    >
    >> given that the function can and cannot (the last case of course) modify
    >> a parameter. I see performance issues (stack related)...
    >>

    > Mostly irrelevant. A C idiom is to always pass structure types by
    > pointer because passing most structures by value means copying, which
    > is indeed a waste of time in most cases, especially if the value isn't
    > modified. While a compiler could optimize this in some cases, it's not
    > expected to, and programmers don't count on it.



    Clear.


    >
    >> - defines: why they use function and variable defines? why they shouldn't?
    >> #define MY_PI 3.1415

    >
    > Established C practice is to use #define for constants since C is a bit
    > puzzling when it comes to constants. Declaring a variable "const" does
    > not make its value a constant expression for purposes of array sizes,
    > for example. Also, memory is allocated for const variables (great
    > oxymoron, incidentally :) when it typically isn't necessary.
    >
    > There's no such thing as a "function" or "variable" define, however.
    > Macros just establish textual replacement rules.



    Yes but...

    >
    >> #define fun(x) ((x) * (x))
    >>

    > Use functions when you can, macros when you have to. The example above
    > is a case of when you don't have to.
    >
    > As for when you need macros, that's not a matter of coding style.


    ....this is puzzling to me. I've seen functions replaced by macros, like this:

    #define xge_mklink_slot(com,from,to,slotfrom,slotto) \
    {\
    tkey _slotfrom=(slotfrom);\
    tkey _slotto =(slotto );\
    \
    CHECKPOINT("ie",getcell((com),(from)).level!=0xff &&
    getcell((com),(to)).level!=0xff);\
    \
    if (!_slotfrom)\
    {\
    if (!(com)->memlink.freelist) realloc_link((com));\
    _slotfrom=(com)->memlink.freelist;\
    (com)->memlink.freelist=getlink((com),_slotfrom).next;\
    ++((com)->memlink.num);\
    \
    if (!getcell((com),(from)).nup)\
    getcell((com),(from)).firstup=_slotfrom;\
    else\

    getlink((com),getcell((com),(from)).lastup).next=_slotfrom;\
    \
    getlink((com),_slotfrom).next=0;\
    getlink((com),_slotfrom).prev=getcell((com),(from)).lastup; \
    getcell((com),(from)).lastup=_slotfrom;\
    ++getcell((com),(from)).nup;\
    }\
    \
    if (!_slotto)\
    ............


    So, I was wondering why and when it's a good idea to use defines.


    --
    Sensei <>

    The difference between stupidity and genius is that genius has its
    limits. (A. Einstein)
    Sensei, Oct 8, 2005
    #12
  13. Sensei

    Sensei Guest

    On 2005-10-07 21:42:37 +0200, Keith Thompson <> said:

    >
    > As for function-like macros, they're sometimes the best way to create
    > inline functions. (C99 has inline functions, but not all compilers
    > support them.) There are also things you can do in a macro that you
    > can't easily do in a function, such as using a type name as an
    > argument. But try not to be too tricky. Macros can be dangerous,
    > since they're expanded in a very early stage of compilation. They can
    > *look* like function calls or variable declarations, but they don't
    > necessarily act like them; for example, they pay no attention to scope.



    Ok, so defines are used to improve performances as inline functions are
    not supported everywhere.


    --
    Sensei <>

    The difference between stupidity and genius is that genius has its
    limits. (A. Einstein)
    Sensei, Oct 8, 2005
    #13
  14. pete <> writes:
    > Keith Thompson wrote:
    >> const double my_pi = 3.14159265358979323848264;

    >
    > Where the hell did you get the idea that the next digit after
    > 3.1415926535897932384 was 8?!!!
    >
    > ;)


    Oops, I meant 6 of course.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
    Keith Thompson, Oct 8, 2005
    #14
  15. Gregory Pietsch said:

    > Sensei wrote:
    >
    >> #define MY_PI 3.1415

    >
    > You might want to extend pi a few more decimal places.


    Why? Observe:

    Circumference of Earth (plus or minus a bit): 40000000 metres.
    Pi = 3.1415
    Radius of Earth: 6366385 metres

    Let's add 1 dp.

    Circumference of Earth (plus or minus a bit): 40000000 metres.
    Pi = 3.14159
    Radius of Earth: 6366203 metres

    Difference from last result: 182 metres. If you're calculating fuel
    requirements for a trip round the entire planet, you'd factor in a
    contingency anyway; you're not going to crash for the sake of 182 metres.

    Let's add another.

    Circumference of Earth (plus or minus a bit): 40000000 metres.
    Pi = 3.141592
    Radius of Earth: 6366199 metres

    Difference from last result: 4 metres. Good enough for nuclear war.

    Let's add another.

    Circumference of Earth (plus or minus a bit): 40000000 metres.
    Pi = 3.1415926 (still comfortably within the ability of a pocket calculator)
    Radius of Earth: 6366198 metres. Difference from last result: 1 metre. Good
    enough for assassinating one mad dictator (or one troublesome priest) from
    the other side of the planet.

    Let's add another.

    Circumference of Earth (plus or minus a bit): 40000000 metres.
    Pi = 3.14159265
    Radius of Earth: 6366198 metres. Difference from last result: 0 metres.
    (Okay, so I'm rounding - but the difference is less than 1 metre, that's
    the point.)

    So who gives a stuff? :)


    --
    Richard Heathfield
    "Usenet is a strange place" - dmr 29/7/2005
    http://www.cpax.org.uk
    email: rjh at above domain (but drop the www, obviously)
    Richard Heathfield, Oct 8, 2005
    #15
  16. Sensei

    pete Guest

    Keith Thompson wrote:
    >
    > pete <> writes:
    > > Keith Thompson wrote:
    > >> const double my_pi = 3.14159265358979323848264;

    > >
    > > Where the hell did you get the idea that the next digit after
    > > 3.1415926535897932384 was 8?!!!
    > >
    > > ;)

    >
    > Oops, I meant 6 of course.


    I've waited over two years for a chance to use that line again.

    http://groups.google.com/group/comp.lang.c/msg/c95e5b2a7f8e39ab

    Thank you.

    --
    pete
    pete, Oct 8, 2005
    #16
  17. pete <> writes:
    > Keith Thompson wrote:
    >> pete <> writes:
    >> > Keith Thompson wrote:
    >> >> const double my_pi = 3.14159265358979323848264;
    >> >
    >> > Where the hell did you get the idea that the next digit after
    >> > 3.1415926535897932384 was 8?!!!
    >> >
    >> > ;)

    >>
    >> Oops, I meant 6 of course.

    >
    > I've waited over two years for a chance to use that line again.
    >
    > http://groups.google.com/group/comp.lang.c/msg/c95e5b2a7f8e39ab
    >
    > Thank you.


    You're welcome. Had I known, I might have made the mistake
    intentionally. :cool:}

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
    We must do something. This is something. Therefore, we must do this.
    Keith Thompson, Oct 8, 2005
    #17
  18. Sensei

    Flash Gordon Guest

    Sensei wrote:
    > On 2005-10-07 21:28:50 +0200, "Mike Wahler" <> said:
    >
    >> "Sensei" <> wrote in message
    >> news:di6gc0$m52$...
    >>
    >>> Hi!
    >>>
    >>> I'm thinking about a good programming style, pros and cons of some
    >>> topics. Of course, this has nothing to do with indentation...
    >>> Students are now java-dependent (too bad) and I need some serious
    >>> motivations for many issues...

    >>
    >> So you're teaching C?

    >
    > Nope. I'm an engineer working on theoretics. I *have* to teach C, and
    > I'm studying before doing anything wrong.


    Well, I hope you have plenty of time, since you have a *lot* to learn.

    >> but you don't appear able to distinguish C from C++

    >
    > Correct. I'm trying.


    Try to learn one language at a time. Trying to learn two, especially two
    with a lot of similarities, is bound to lead to confusion.

    >> and you don't appear to be aware of the consensus about
    >> premature optimization

    >
    > My biggest programming efforts have always been on assembly. I'm used to
    > do a shift instead of dividing or multiplying by 2. I've read it's not
    > useful on high level languages like C.


    What you have read is correct. Modern compilers have an optimiser to do
    this for you.

    > If a variable is passed on the stack, I imagine *i pushes an address,
    > while i pushed the entire data, so if i is a big structure is not a nice
    > thing.


    Yes, that is correct. With a large structure you would tend to pass a
    pointer to avoid copying a lot of data.

    > Moreover, I don't know if C says *how* parameters are passed to
    > functions: stack, pointer, whatever...


    C does not specify a stack. All it specifies is that parameters are
    passed by value (even pointers). On at least some systems parameters
    will be passed in registers.

    >>> - defines: why they use function and variable defines? why they
    >>> shouldn't?
    >>> #define MY_PI 3.1415
    >>> #define fun(x) ((x) * (x))

    >>
    >> and you don't appear to be aware of the type-checking that a
    >> real function can give, and that macros do not.

    >
    > Of course macros are macros. No checks, no functions. Why using one
    > instead of another is not clear to me. As I said, I'm an assembly guy,
    > and ``functions'' do not really exist.


    Type checking is a wonderful thing, it allows the compiler to complain
    if you do something obviously stupid. Such as passing a pointer to a
    function requiring an integer.

    > You don't appear to understand that people could ask for informations
    > here about std-c with different knowledge :)


    We are quite used to that.

    I strongly suggest you work through K&R2 and read the FAQ for this group
    (which will explain what K&R2 is).

    Also remember that high level languages were invented to make
    programming easier, and this means you should forget about using a lot
    of the little tricks you use as an assembler programmer.
    --
    Flash Gordon
    Living in interesting times.
    Although my email address says spam, it is real and I read it.
    Flash Gordon, Oct 8, 2005
    #18
  19. Sensei

    pete Guest

    Sensei wrote:

    > So, I was wondering why and when it's a good idea to use defines.


    If a #define can make the source code easier to read,
    then that is why and when.

    --
    pete
    pete, Oct 8, 2005
    #19
  20. Sensei

    Skarmander Guest

    Sensei wrote:
    > On 2005-10-07 21:35:26 +0200, Skarmander <> said:
    >
    >> Sensei wrote:
    >>

    <snip>
    >>> #define fun(x) ((x) * (x))
    >>>

    >> Use functions when you can, macros when you have to. The example above
    >> is a case of when you don't have to.
    >>
    >> As for when you need macros, that's not a matter of coding style.

    >
    >
    > ...this is puzzling to me. I've seen functions replaced by macros, like
    > this:
    >


    > #define xge_mklink_slot(com,from,to,slotfrom,slotto) \
    > {\
    > tkey _slotfrom=(slotfrom);\
    > tkey _slotto =(slotto );\
    > \
    > CHECKPOINT("ie",getcell((com),(from)).level!=0xff &&
    > getcell((com),(to)).level!=0xff);\
    > \
    > if (!_slotfrom)\
    > {\
    > if (!(com)->memlink.freelist) realloc_link((com));\
    > _slotfrom=(com)->memlink.freelist;\
    > (com)->memlink.freelist=getlink((com),_slotfrom).next;\
    > ++((com)->memlink.num);\
    > \
    > if (!getcell((com),(from)).nup)\
    > getcell((com),(from)).firstup=_slotfrom;\
    > else\
    >
    > getlink((com),getcell((com),(from)).lastup).next=_slotfrom;\
    > \
    > getlink((com),_slotfrom).next=0;\
    >
    > getlink((com),_slotfrom).prev=getcell((com),(from)).lastup; \
    > getcell((com),(from)).lastup=_slotfrom;\
    > ++getcell((com),(from)).nup;\
    > }\
    > \
    > if (!_slotto)\
    > ...........
    >

    This is absolutely horrible. Words fail me. Whoever wrote this should be
    shot.

    Let's go over why this is a bad idea:

    - In a macro, arguments are not checked against their types, as there
    are no types. This means that the function is both harder to call ("com"
    must be a pointer, but of what type?) and the compiler can offer less
    help checking validity.

    - Generalizing, the validity of a macro simply cannot be checked on its
    own. Only uses of it can be checked. Errors or warnings that occur from
    such a use could be either due to bad use or a bad macro, and it'll be
    hard to tell which is which.

    - Unlike functions, macros use textual replacement for their arguments.
    If an argument uses a side-effect (it modifies the program state when
    evaluated) then you typically cannot use it. For example:
    xge_mklink_slot(c, f++, t++, slf, slt);
    This does not have the same effect as the equivalent function call
    would: every time "from" is replaced in the macro it is replaced with
    "f++". That is, f will be incremented every time "from" is used!

    - This is very likely to *harm* performance. Yes, you read that right.
    If this macro is invoked multiple times, the great big block of code it
    stands for is inserted *every time*. Do this a few times and you'll get
    code that is huge. And WHY? Function calls are not nearly as expensive
    as most people like to pretend. It is in fact likely this code will run
    slower because big code means the instruction cache most modern
    processors are equipped with cannot be effectively used. Never try to
    outsmart the compiler. Only educate it in things you know it cannot see.
    Making decisions on when to inline functions is something a compiler is
    qualified to do, and only code profiling should be admissible evidence
    to the contrary.

    Apropos inlining: almost all compilers I know offer a way to indicate
    that inlining is desirable (or even force it where possible). The new
    C99 standard adds an "inline" keyword, and most pre-C99 compilers
    support an "inline", "_inline", "__inline" keyword or some variation
    thereof. Macros are almost never necessary or desirable to get an
    inlined function.

    All that said, there is one purpose this macro could still serve that
    cannot be achieved otherwise in C: precisely because the arguments have
    no types, it can be used as a template to produce similar functions that
    differ only in the type of the arguments. This is what templates
    accomplish in C++ and generics accomplish in the most recent versions of
    Java.

    But even so, it should only be used in a way that does not compromise
    type safety, by defining the type-safe functions:

    /* Unlike XGE_MKLINK_SLOT, the compiler will warn or stop if the
    argument types do not match the formal parameter types. */
    [return-type] xge_mklink_slot(xge_com* com, xge_cell from, xge_cell to,
    tkey slotfrom, tkey slotto) {
    return XGE_MKLINK_SLOT(com, from, to, slotfrom, slotto);
    }

    Here [return-type] I cannot know, and the xge_com and xge_cell types are
    invented by me. I capitalized XGE_MKLINK_SLOT because macro names
    *should* be capitalized to prevent confusion: as I've said, using a
    macro is not the same thing as calling a function, and it's dangerous to
    pretend that it is.

    Even this technique is dubious because xge_mklink_slot is a highly
    specific macro, and it's unlikely it's used in the way I sketched above.
    Note also that in C, generic functions are usually written by passing
    around generic pointers. Consult your C book for more information; it
    will probably mention the qsort() function as an example.

    This macro *should* be rewritten to a function. I cannot conceive of any
    reason why a macro would be preferrable in this case. Do you understand
    why I said it's not a matter of coding style? It's a matter of what you
    want to achieve, not how it looks. "Style", if it comes into play at
    all, is to prefer functions to macros consistently, and being ready to
    justify your use of a macro.

    > So, I was wondering why and when it's a good idea to use defines.
    >

    Don't think in terms of "good idea/bad idea". Learn what purpose and
    what drawbacks each feature has, then apply this knowledge in every case
    to achieve the best result. In case of macros, the drawbacks associated
    with them very often outweigh the benefits, especially when it comes to
    replacing functions.

    I can give you the short version, of course, but keep in mind that these
    are only rules of thumb and not absolute commandments. Use a macro for:

    - defining symbolic constants that are not part of an enumerated type.
    Example: #define M_PI 3.1415926536
    - encapsulating system-specific constructs that cannot be abstracted by
    a function. Example: #define INLINE __inline
    - allowing header files to be included more than once without error.
    Example:
    #ifndef HEADER_H
    #define HEADER_H
    ...contents of "header.h"
    #endif

    These are the basic established "good ideas" for macros. There are other
    circumstances in which experienced C programmers will use macros, but
    you should leave them until you can understand why. I won't attempt to
    explain it all in a Usenet post; consult the books written by people
    who've had more time than me to think about it.

    S.
    Skarmander, Oct 8, 2005
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Fredrik Lundh
    Replies:
    4
    Views:
    392
  2. Astley Le Jasper

    Good programming style

    Astley Le Jasper, Sep 12, 2008, in forum: Python
    Replies:
    5
    Views:
    301
    George Sakkis
    Sep 15, 2008
  3. Ken Varn
    Replies:
    0
    Views:
    428
    Ken Varn
    Apr 26, 2004
  4. Vincent Foley
    Replies:
    2
    Views:
    117
    Ilmari Heikkinen
    Apr 29, 2005
  5. Replies:
    52
    Views:
    1,025
    88888 Dihedral
    Apr 6, 2013
Loading...

Share This Page