Preprocessor

In the following code
[code]
#define a b
#define b c
[/code]
will a be replaced by c, or will only b in the original be replaced?

I.e., does
[code]
y = a*x + b ; // original code
[/code]
become
[code]
y = b*x + c ; // what I intended
[/code]
or does it become
[code]
y = c*x + c ; // not what I want
[/code]

«1

Comments

  • It becomes y = c*x + c ;

    #defines are just text replacement. Might want to concider using const variables instead.
  • : In the following code
    : [code]:
    : #define a b
    : #define b c
    : [/code]:
    : will a be replaced by c, or will only b in the original be replaced?
    :
    : I.e., does
    : [code]:
    : y = a*x + b ; // original code
    : [/code]:
    : become
    : [code]:
    : y = b*x + c ; // what I intended
    : [/code]:
    : or does it become
    : [code]:
    : y = c*x + c ; // not what I want
    : [/code]:
    :
    :
    I'm not sure, but perhaps this would do the trick:
    [code]
    #define b c
    #define a b
    [/code]
    This might change b into c and then change a into b. I don't know if the preprocessor keeps on iterating through all the #defines until it cannot make any more changes.
  • Is there any way to look at the preprocessor output before the compiler gets it?

  • : Is there any way to look at the preprocessor output before the
    : compiler gets it?
    :
    :

    Yes--It depends on the toolchain or IDE that you are using.

    Also, I if C++, I second using const int's over #define, as #define is coinsidered bad practice in C++ for constant expressions.

    If C, I would recommend not doing that in the first place, and choosing better names as well...
    [hr][size=1][leftbr].:EvolutionEngine[rightbr][leftbr].:MicroOS Operating System[rightbr][leftbr][link=http://www.brokenthorn.com]Website :: OS Development Series[rightbr][/link][/size]
  • I've found that my preprocessor requires that the first field in a #define be an identifier. The text I'm using points out the following error
    [code]
    if (x = 0) /* error */
    [/code]
    when the author meant
    [code]
    if (x == 0) /* correct */
    [/code]
    and suggests the following to help prevent it
    [code]
    #define EQ ==

    if (x EQ 0) /* preprocessor converts EQ to == */
    [/code]
    Being an old Fortran programmer I'd rather use
    [code]
    #define .eq. ==
    [/code]
    but the preprocessor rejects it because .eq. is not an identifier. Is this standard behavior for a preprocessor? Or just TC 2.0?

  • : but the preprocessor rejects it because .eq. is not an identifier.
    : Is this standard behavior for a preprocessor? Or just TC 2.0?
    :

    . is a reserved operator in C, and in particular the naming conventions do not allow for .'s to be used in names.

    Best Regards,
    Richard

    The way I see it... Well, it's all pretty blurry
  • I would strongly advise against inventing your own operators. Re-inventing the C language is considered very poor programming and that author should know better.

    To avoid the problem described, get a good compiler which will warn for assignment inside conditions, aka "possible incorrect assignment". Or alternatively, write the code like this:

    if(0 == x)

    because this will never compile:

    if(0 = x)


    Intentional assignment inside conditions is also considered rather poor programming as it leads to unreadable code, and there is never a reason to use it.
  • : I would strongly advise against inventing your own operators.
    : Re-inventing the C language is considered very poor programming and
    : that author should know better.
    The syntax of the C language is so poorly designed that a little syntactic sugar can only help. In particular, in the absence of strong typing, such similar operators as = for assignment and == for relational is a poor choice. The tools are there to correct it so why not?

    :
    :
    : Intentional assignment inside conditions is also considered rather
    : poor programming as it leads to unreadable code, and there is never
    : a reason to use it.

    I agree but it seems to be a recommended practice in C textbooks. E.g.,
    [code]
    while ((c = getchar()) != EOF)
    {
    /*
    code here
    */
    }
    [/code]
    I don't think you'd have to look far in too many textbooks to find precisely that example. Actually I think you'll find the above, or something very close to it, in Kernighan and Ritchie's [italic]The C Programming Language[/italic]. One alternative to the above is
    [code]
    c = getchar() ;
    while (c != EOF) // the Pascal strategy
    {
    /*
    code here
    */
    c = getchar() ;
    }
    [/code]
    which (1)repeats a statement and (2)places the repeated statement at the bottom of the loop where it is not as visible. Kernighan included this example, of something close to it, in his rant [italic]Why Pascal Is Not My Favorite Programming Language[/italic](1981).

    Another possibility is
    [code]
    for (c = getchar() ; c != EOF ; c = getchar())
    {
    /*
    code here
    */
    }
    [/code]
    which places both instances of the assignment statement on the same line. This problem is that at first glance it seems to be an indexed loop but is actually a while loop in disguise, and IMHO worse than what it seeks to cure.

    Finally I consider this to be the worst solution.
    [code]
    while (1)
    {
    c = getchar() ;
    if (c == EOF)
    break ;
    /*
    code here
    */
    }
    [/code]

  • : : but the preprocessor rejects it because .eq. is not an identifier.
    : : Is this standard behavior for a preprocessor? Or just TC 2.0?
    : :
    :
    : . is a reserved operator in C, and in particular the naming
    : conventions do not allow for .'s to be used in names.
    :
    : Best Regards,
    : Richard
    :
    : The way I see it... Well, it's all pretty blurry

    Right, but the question is "do all preprocessors enforce this rule?" I actually find this rather surprising since it requires the preprocessor to know something of the C language, which must increase the complexity of the preprocessor code. All it really needs to know about is literals since you don't want text replacement inside literals. The code for recognizing literals is easy.


  • : Right, but the question is "do all preprocessors enforce this rule?"

    I believe it is specified by the C standard, so any compiler following the standard would implement it the same way. Even with compilers deviating from the standard, I don't think the preprocessor definitions are treated differently.

    : I actually find this rather surprising since it requires the
    : preprocessor to know something of the C language, which must
    : increase the complexity of the preprocessor code. All it really
    : needs to know about is literals since you don't want text
    : replacement inside literals. The code for recognizing literals is
    : easy.
    :

    What you are suggestion would create a very interesting preprocessor. Say I could define:
    [code]
    #define (

    ...

    void foo()
    {
    return;
    }
    [/code]
    Would then neatly replace ( with nothing, generating a compiler error.

    In fact, it's logical that the preprocessor needs to know the language elements as well as the compiler. It needs to determine what the seperate symbols are if it is to properly replace them.
    Also, one can probably use much of the same code for the preprocessor and the compiler, so that's not an argument to make the preprocessor a simple textual replacer.

    Best Regards,
    Richard

    The way I see it... Well, it's all pretty blurry
  • : The syntax of the C language is so poorly designed that a little
    : syntactic sugar can only help. In particular, in the absence of
    : strong typing, such similar operators as = for assignment and == for
    : relational is a poor choice. The tools are there to correct it so
    : why not?

    I suspect you have never worked on a large scale project, or with a team of several programmers before.

    The purpose of the preprocessor is not to correct anything at all.

    This will be debugging hell. Using some simple "syntactic sugar" does not change anything as it will be still--in the end--be C or C++. Do to heavy use of this abstracted "syntactic sugar", we would have *no idea* what the produced software is, and it will become *very* hard to run through a debugger.

    If you dont like C or C++, dont create another useless abstraction ontop of it - use a different language, instead.

    [b]On a related note[/b]

    Some software (including my engine) has actually created basic keywords for the language using #define to help better identify certain things and to improve readability. For example, we use _in, _out, and _opt in parameter names as the language itself does not provide any way of determining input/output parameters.

    If you can improve the language, go for it. However, do so in not abstracting what C and C++ can do alone.

    : I agree but it seems to be a recommended practice in C textbooks.

    This is why I do not like alot of textbooks. So many textbooks are just as bad as so many tutorials in teaching bad programming practices.

    The focus of C and C++ textbooks is usually to teach how the language is designed and work, [b]not to teach programming[/b]. There is a difference between programming languages and programming. In standard practice, it is recommended against this method, even if textbooks use it.

    Probably the worst example I have seen of this is Andre Lamothes' Tricks books. They are great books, but teach very old and outdated practices, and promotes bad programming habits in hopes of "making it easier to understand". I am thinking alot of textbooks do this.

    [hr][size=1][leftbr].:EvolutionEngine[rightbr][leftbr].:MicroOS Operating System[rightbr][leftbr][link=http://www.brokenthorn.com]Website :: OS Development Series[rightbr][/link][/size]
  • : : I would strongly advise against inventing your own operators.
    : : Re-inventing the C language is considered very poor programming and
    : : that author should know better.
    : The syntax of the C language is so poorly designed that a little
    : syntactic sugar can only help. In particular, in the absence of
    : strong typing, such similar operators as = for assignment and == for
    : relational is a poor choice. The tools are there to correct it so
    : why not?


    Because it makes the code as pain to read for other programmers, and it makes the code a pain to debug for anyone. If you remove a possible hazard, but adds confusion instead, you have created a larger hazard than the original one, because the original one is known to every C programmer.

    Code inside macros is poor programming, you will find that the vast majority of the programmers out there agree.


    : : Intentional assignment inside conditions is also considered rather
    : : poor programming as it leads to unreadable code, and there is never
    : : a reason to use it.
    :
    : I agree but it seems to be a recommended practice in C textbooks.

    I completely agree with MT2002, text books are there to teach the language, and not necessarily good programming practice. As a C programmer, you should know that assignment inside conditions is allowed. That doesn't automatically mean that you should use it. C allows a lot of pointless, confusing syntax.

    And some poor programmers take pride in using rare, unusual syntax, as they think that programming skill equals how much of the programming language syntax you know. Recursion is a very common example of this.


    : [code]:
    : while ((c = getchar()) != EOF)
    : {
    : /*
    : code here
    : */
    : }
    : [/code]:
    : I don't think you'd have to look far in too many textbooks to find
    : precisely that example. Actually I think you'll find the above, or
    : something very close to it, in Kernighan and Ritchie's [italic]The C
    : Programming Language[/italic].

    Of course you will find it in K&R, they invited assignment inside conditions, so they must motivate the use of it. You should also be aware of that the K&R book and their arguments are from the days when programmers were encouraged to write as few symbols in the source code as possible, to save disk space.

    Lets rewrite the loop more compact, to make it clearer why it might not be such a good idea:

    while((c=getchar())!=EOF)

    Easy to read?


    I think most will agree that the second example you posted is better, as it is more readable. The second example also allows the compiler/code analyzer to search for your original problem, confusing = and ==.

    Both examples are pretty much equally efficient.


    : [code]:
    : c = getchar() ;
    : while (c != EOF) // the Pascal strategy
    : {
    : /*
    : code here
    : */
    : c = getchar() ;
    : }
    : [/code]:
    : which (1)repeats a statement and (2)places the repeated statement at
    : the bottom of the loop where it is not as visible. Kernighan
    : included this example, of something close to it, in his rant
    : [italic]Why Pascal Is Not My Favorite Programming
    : Language[/italic](1981).


    "Not as visible"?? That's not even an argument, when you read code, you must read all of it.


    : Another possibility is
    : [code]:
    : for (c = getchar() ; c != EOF ; c = getchar())
    : {
    : /*
    : code here
    : */
    : }
    : [/code]:


    That's just obfuscation. Yes, it is a very poor version.


    : Finally I consider this to be the worst solution.
    : [code]:
    : while (1)
    : {
    : c = getchar() ;
    : if c == EOF
    : break ;
    : /*
    : code here
    : */
    : }
    : [/code]:

    Yes, also a poor example, though I think the for-loop was worse. :-)
  • : And some poor programmers take pride in using rare, unusual syntax,
    : as they think that programming skill equals how much of the
    : programming language syntax you know. Recursion is a very common
    : example of this.

    Are you saying that recursion is bad programming? If so, why? Some algorithms are a natural for recursion, the quick sort probably being the most well known. Try coding quick sort without recursion. It can be done. I've done it in Fortran IV, which does not allow recursion, but the resulting code is more complex.



  • : Are you saying that recursion is bad programming? If so, why? Some
    : algorithms are a natural for recursion, the quick sort probably
    : being the most well known. Try coding quick sort without recursion.
    : It can be done. I've done it in Fortran IV, which does not allow
    : recursion, but the resulting code is more complex.

    It can be both hard to read snd debug. Recursion is okay for some routines so long as it is clearly readable. However, one should opt for code readability, so long as that algorithms speed is not a factor. On pcs, it useually isn't a factor.
    [hr][size=1][leftbr].:EvolutionEngine[rightbr][leftbr].:MicroOS Operating System[rightbr][leftbr][link=http://www.brokenthorn.com]Website :: OS Development Series[rightbr][/link][/size]
  • If you would like to learn more about programming practices, I would personally recommend picking up Effective C++, Effective STL, and Code Complete - all great books on software design and C++ practices.

    [hr][size=1][leftbr].:EvolutionEngine[rightbr][leftbr].:MicroOS Operating System[rightbr][leftbr][link=http://www.brokenthorn.com]Website :: OS Development Series[rightbr][/link][/size]
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories