#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2013
    Posts
    142
    Rep Power
    2

    Constant macros as function parameters


    C:\Programming\Files\Chapter 8\Programming Projects\2\get_corners.c|5|error: expected ';', ',' or ')' before numeric constant
    my textbook never mentioned about constants not being able to be function parameters, so I put in one in the function prototype.
    then I got this error.

    replacing the constant with a normal parameter returned no error, so I know the macro parameter caused the error.
    is this because macros are like actual numbers, so declaring
    [code]
    void sum(int x[], int SIZE)
    [code]
    where SIZE is a constant macro, would be like saying
    Code:
    void sum(int x[], 3)
    ?
  2. #2
  3. Contributed User
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    Jun 2005
    Posts
    4,407
    Rep Power
    1871
    Well the better thing to do would be to write symbol names in lower case (or mixed case if you prefer).
    Code:
    void sum(int x[], int size); // prototype
    
    void sum(int x[], int size) { // implementation
    }
    Now an actual function invocation using your macro value would be
    Code:
    sum(someArray,SIZE);
    By convention, macros are always written in UPPERCASE and all other symbols are in lowercase or MixedCase. The reason being that if you do use uppercase for symbols (your example), you run the risk of random substitutions. Worst of all are substitutions which result in valid code which does something that you didn't expect. These can be a real PITA to track down.

    The C pre-processor and C compiler are in effect implementing different programming languages on the same source code. For this reason, you should keep all the pre-processor symbols in a different namespace to all the C symbols. As stated above, this namespace separation is usually achieved by using all uppercase for the pre-processor, and lower/mixed for C.

    Example
    Code:
    #if DEBUG
    #define SIZE 10
    #else
    #define SIZE 100
    #endif
    
    void sum ( int x[], int size ) {
    }
    
    int main ( ) {
        int array[SIZE];
        sum(array,SIZE);
    }
    The C pre-processor runs first, and deals with all the red bits.

    This is what the C compiler sees, if you compile with say gcc -DDEBUG prog.c
    Code:
    void sum ( int x[], int size ) {
    }
    
    int main ( ) {
        int array[10];
        sum(array,10);
    }
    This is what the C compiler sees, if you compile with say gcc prog.c
    Code:
    void sum ( int x[], int size ) {
    }
    
    int main ( ) {
        int array[100];
        sum(array,100);
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper
  4. #3
  5. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,223
    Rep Power
    2222
    Now armed with knowledge about the preprocessor courtesy of salem, look at what you were attempting to do:
    Originally Posted by 046
    void sum(int x[], int SIZE)
    [code]
    where SIZE is a constant macro, would be like saying
    [code]
    void sum(int x[], 3)
    From this, I assume that your macro was
    #define SIZE 3
    , in which case the preprocessor replaced all instances of SIZE with 3. And clearly, the parser was expecting to see a datatype in that parameter list and not a literal value.

    You could have defined SIZE differently, eg
    #define SIZE an_int_var
    in which case
    void sum(int x[], int SIZE)
    would have expanded to
    void sum(int x[], int an_int_var)
    which would have been valid syntax and would have been acceptable to the compiler. However, that would also be a fairly stupid way to declare a formal parameter.

    However, another similar practice is widely used; eg:
    Code:
    #define MYINT int
    
    void sum(int x[], MYINT size)
    In that case, the function header would expand to:
    void sum(int x[], int size)
    which is perfectly acceptable.

    Some programmers will take type definitions and replace them with macros, such as
    #define PCHAR char*
    or
    #define SATELLITES struct Satellites
    That keeps them from having to type everything out all the time, but I would much rather use typedef's for that purpose as we do widely at work to name struct types and hence to not have to always include the keyword struct.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2013
    Posts
    142
    Rep Power
    2
    @salem
    although I still need to do a bit more researching to understand what a namespace is, I did understand the reason that you distinguish between cases is to substitute the appropriate values in the appropriate identifiers, as well as the relationship between the preprocessor and compiler.

    also, this was new to me; I didn't know you could have if statements in the preprocessor directive.
    Code:
    #if DEBUG
    #define SIZE 10
    #else
    #define SIZE 100
    #endif
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2013
    Posts
    142
    Rep Power
    2
    @dwise_aol
    And clearly, the parser was expecting to see a datatype in that parameter list and not a literal value.
    this was the key answer to my question, thank you.

    Some programmers will take type definitions and replace them with macros, such as
    #define PCHAR char*
    or
    #define SATELLITES struct Satellites
    That keeps them from having to type everything out all the time, but I would much rather use typedef's for that purpose as we do widely at work to name struct types and hence to not have to always include the keyword struct.
    I learned about typedefs: that they are types defined by the programmer, and that they make the program more easier to understand.
    (although I'm not building such complex programs as for me to appreciate the use of enumerated types yet)

    but what would be the purpose of replacing an already defined type like int or char * with a macro?


    btw, I checked my logs for warnings after executing the program to verify your replacement examples.
    in replacing the directives, I deleted #include <stdio.h>, resulting in the message "implicit definition for function printf".
  10. #6
  11. Banned ;)
    Devshed Supreme Being (6500+ posts)

    Join Date
    Nov 2001
    Location
    Woodland Hills, Los Angeles County, California, USA
    Posts
    9,643
    Rep Power
    4247
    Originally Posted by 046
    but what would be the purpose of replacing an already defined type like int or char * with a macro?
    Most of the time, people do this to write cross-platform code, especially with older C code. For example, say I need a 64-bit variable. In 64-bit linux with gcc compiler, this can be handled by a long type, but in 32-bit linux (gcc compiler) or Windows (Visual C++ compiler), a long isn't big enough and I have to declare the variable as long long. In order to keep the code cross-platform, I could do:
    Code:
    #ifdef __x86_64__
    // 64-bit gcc
    #define LONG long
    #elif defined(_WINDOWS_)
    #define LONG long long
    #else
    #define LONG long long
    #endif
    
    LONG my_variable;
    Now depending on the compiler being used, my_variable will either be declared as long or long long. However, it will always be able to hold 64 bits. This makes the code portable across multiple platforms and compilers.

    With newer C compilers, such constructs are not necessary as it now has types such as i8, i16, i32, i64 etc. to declare 8-bit, 16-bit, 32-bit or 64-bit type (and unsigned equivalents u16, u32, u64 etc.)

    Also, it is better to use typedef instead of #define for such constructs anyway. For example:
    Code:
    #define INT_PTR int *
    typedef int * int_ptr
    These two look equivalent (i.e. both are declaring an alias for int-pointer), but they are not. See what happens when you declare multiple variables like this:
    Code:
    INT_PTR a, b, c;
    int_ptr d, e, f;
    In the above code, d, e and f are all int pointers, since the int_ptr typedef applies to all of them. However, the INT_PTR macro is replaced by the preprocessor before the compiler sees it and therefore, only a is an int pointer, b and c are both integer variables (i.e. the compiler sees the code as)
    Code:
    int *a, b, c;
    If the programmer intended to declare a, b and c as int pointers, the code is certainly not doing that and the bug is very hard to detect by inexperienced programmers.

    Originally Posted by 046
    btw, I checked my logs for warnings after executing the program to verify your replacement examples.
    in replacing the directives, I deleted #include <stdio.h>, resulting in the message "implicit definition for function printf".
    It is always a good idea to compile your code with max. warnings level turned on. Warnings are nearly always caused by suspect code. Also, function prototypes are a good thing :).
    Up the Irons
    What Would Jimi Do? Smash amps. Burn guitar. Take the groupies home.
    "Death Before Dishonour, my Friends!!" - Bruce D ickinson, Iron Maiden Aug 20, 2005 @ OzzFest
    Down with Sharon Osbourne

    "I wouldn't hire a butcher to fix my car. I also wouldn't hire a marketing firm to build my website." - Nilpo
  12. #7
  13. Contributing User

    Join Date
    Aug 2003
    Location
    UK
    Posts
    5,116
    Rep Power
    1803
    A parameter is by definition a variable.

    If a function uses some constant value, it need not be passed as a parameter at all - it can be used directly in the function.

    If you need to have have both constant and variable forms of the same function then you need two functions; perhaps:

    Code:
    void sum( int x[], int size ) ;
    
    void sum3( int x[] )
    {
        sum( x, SIZE  ) ;
    }
    If you were to use C++ you could have a single function thus:

    Code:
    void sum( int x[], int size = SIZE ) ;
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2013
    Posts
    142
    Rep Power
    2
    @Scorpions4ever
    Most of the time, people do this to write cross-platform code, especially with older C code.
    so you have a specific requirement for the data type (it needs to have 64 bits).
    however, depending on the platform, the names of the pre-defined types meeting that requirement.
    but it would be cumbersome/impractical to change the data type in the code depending on what platform it is run.
    therefore you define a new variable LONG , which will have 64 bits regardless of platform.

    @Clifford
    A parameter is by definition a variable.
    I think I didn't have this definition clear enough in myself, and maybe that's why I got confused.
    thank you.

IMN logo majestic logo threadwatch logo seochat tools logo