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

    Join Date
    Dec 2013
    Posts
    2
    Rep Power
    0

    #define, #ifdef... #endif


    Hi all,
    i was learning C, i understood how #ifdef...#endif works.
    but i didnt understand, when and where we will need this.

    if we #defined something,then we defined, why we use #ifdef again. we already know if it is defined or not, i am confused.
    thanks for your asnwers.
  2. #2
  3. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,145
    Rep Power
    2222
    In a simple one-file student's programming assignment, you would not need to use conditional compilation (that is what it is called, so now you have the keywords for future searches and discussions). But the real world has very little in common with a simple one-file student's programming assignment.

    In the real world, software projects consist of many files, even hundreds of them. And usually similar projects will have many files in common with other projects, so those projects will share those common files -- one reason why this is desirable is that if a bug is discovered and fixed in one project, then you would want that fix to apply to all the projects that share that file.

    However, often those common files will differ ever so slightly from one project to the next. For example, while all projects will have the same array, the size of the array could differ from project to project. So when that happens do you scrap the entire idea of having common files? Or do you use conditional compilation to make those array sizes different for the different projects?

    if we #defined something,then we defined, why we use #ifdef again. we already know if it is defined or not, i am confused.
    When you wrote those over-a-hundred source files, you did not know which macros would be #define'd and which would not, but rather you wrote the code thinking "When this macro is #define'd, I'll do this, but when it's not then I'll do that." It isn't until you build an individual project that you will select whether or not to #define a macro. For example, you could create a special header file that only contains a project's #define and #undef statements and you would have all your source files #include that header file.

    I repeat: until you actually build a specific project, you will not know whether something is #define'd or not.


    Here's a real-world example in one file. On my site I discuss sockets programming and I provide some source files for simple servers and clients for the echo service (RFC 862). While sockets is a UNIX invention (BSD UNIX), it has been ported over to Windows as Winsock. Winsock is very much like BSD sockets, except for the several places where it is not. If you write a Winsock client, then you will be unable to compile that code on Linux, and if you wrote a UNIX client then you will not be able to compile it on Windows.

    I wrote the following code so that it could be compiled either on Windows or on Linux. All you need to change is whether you #define or #undef the macro, WINSOCK_EXAMPLE. You may consider this code as an example of using conditional compilation in a single-file program -- prologue comment block removed for brevity:
    Code:
    /* #define for Windows; #undef for UNIX/Linux */
    #define WINSOCK_EXAMPLE
    
    #include <stdlib.h>     /* for exit() */
    #include <stdio.h>      /* for printf(), fprintf() */
    #include <string.h>     /* for string functions  */
    #ifdef WINSOCK_EXAMPLE
    #include <winsock2.h>    /* for socket(),... */
    #else
    #include <unistd.h>     /* for close() */
    #include <sys/socket.h> /* for socket(),... */
    #include <netinet/in.h> /* for socket(),... */
    #include <arpa/inet.h>  /* for inet_addr() */
    #endif    
    
    #define RCVBUFSIZE 256   /* Size of receive buffer */
    #define ECHO_PORT   7    /* Default standard port number for echo   */
    
    void ConductEchoSession(int sock);       /* split echo protocol off from basic sockets */
    void ReportError(char *errorMessage);   /* Error handling function (no exit) */
    void DieWithError(char *errorMessage);  /* Fatal Error handling function     */
    void chomp(char *str);                  /* remove newline character(s)       */
    
    /********************************************************************/
    /* main -- like opinions, every program has one.                    */
    /********************************************************************/
    int main(int argc, char *argv[])
    {
        int sock;                        /* Socket descriptor */
        struct sockaddr_in echoServAddr; /* Echo server address */
        unsigned short echoServPort;     /* Echo server port */
        char *servIP;                    /* Server IP address (dotted quad) */
    #ifdef WINSOCK_EXAMPLE
        WORD wVersionRequested;          /* Version of Winsock to load */
        WSADATA wsaData;                 /* Winsock implementation details */ 
    #endif    
    
        /* First process the command-line arguments. */
        
        /* Test for correct number of arguments */
        if ((argc < 2) || (argc > 3))    
        {
            fprintf(stderr, "Usage: %s <Server IP> [<Echo Port>]\n", argv[0]);
            exit(1);
        }
    
        /* first arg: server IP address (dotted quad) */
        servIP = argv[1];             
    
        /* second arg, if any: port number */
        if (argc == 3)
            echoServPort = atoi(argv[2]);   /* Use given port */
        else
            echoServPort = ECHO_PORT;  /* otherwise, use the default port number */
    
    #ifdef WINSOCK_EXAMPLE
        /* Winsock DLL and library initialization  */
        wVersionRequested = MAKEWORD(2, 0);   /* Request Winsock v2.0 */
        if (WSAStartup(wVersionRequested, &wsaData) != 0) /* Load Winsock DLL */
        {
            fprintf(stderr,"WSAStartup() failed");
            exit(1);
        }
    #endif    
    
    /* 1. Create a socket. */
        /* Create a reliable, stream socket using TCP */
        if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
            DieWithError("socket() failed");
    
    /* 2. Connect to the server. */
        /* Construct the server address structure */
        memset(&echoServAddr, 0, sizeof(echoServAddr));     /* Zero out structure */
        echoServAddr.sin_family      = AF_INET;             /* Internet address family */
        echoServAddr.sin_addr.s_addr = inet_addr(servIP);   /* Server IP address */
        echoServAddr.sin_port        = htons(echoServPort); /* Server port */
    
        /* Establish the connection to the echo server */
        if (connect(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
            DieWithError("connect() failed");
        else
            printf("Connected to Server %s\n",servIP);
    
        /* connected, so perform the echo session */
        /* Will perform the calls to send(), recv(), shutdown, and close */
        ConductEchoSession(sock);
    
    #ifdef WINSOCK_EXAMPLE
        /* Clean up Winsock */
        WSACleanup();  
    #endif    
    
        return 0;
    }
    
    
    /********************************************************************/
    /* ConductEchoSession                                               */
    /*    Conduct an echo session with the server.                      */
    /*    Spun off as a separate function solely for instructional      */
    /*            purposes:                                             */ 
    /*        1. to simplify the main function                          */
    /*        2. to separate the echo protocol from the basic sockets   */
    /*            sockets operations as much as possible.               */
    /********************************************************************/
    void ConductEchoSession(int sock)
    {
        int echoStringLen;               /* Length of string to echo */
        char echoBuffer[RCVBUFSIZE];     /* Buffer for echo string */
        char msgBuffer[RCVBUFSIZE];      /* Buffer for input string */
        int bytesRcvd, totalBytesRcvd;   /* Bytes read in single recv() and total bytes read */
    
    /* 5. Repeat the send and receive as required. */
        /* repeat loop until either the server disconnects or the user quits */
        do  /* while (bytesRcvd && (echoStringLen > 1)) */
        {
            /* input the message string from the user */
            
            /* use fgets on stdin instead of gets to prevent buffer overflow */
            printf("Enter Message: ");
            fgets(msgBuffer,RCVBUFSIZE,stdin);
    
            /* Remove newline char(s) from end of input string */
            /*    (is necessary with fgets())                  */
            chomp(msgBuffer);       
            
            /* Determine input-string length, plus one for null-terminator */
            echoStringLen = strlen(msgBuffer) + 1;          
    
            /* Inputting an empty string (length == 1) quits, so proceed   */
            /*      only if input string was not empty                     */        
            if (echoStringLen > 1)
            {
    /* 3. Send (send()) the request. */
                /* Send the string, including the null terminator, to the server */
                if (send(sock, msgBuffer, echoStringLen, 0) != echoStringLen)
                    DieWithError("send() sent a different number of bytes than expected");
    
    /* 4. Receive (recv()) the server's response. */
                /* Receive the same string back from the server                 */
                /* The return message might not all come back in one piece,     */
                /*      especially if the message was long.                     */
                /* Therefore, loop as long as we haven't gotten the entire      */
                /*      message back; ie while (totalBytesRcvd < echoStringLen) */
                totalBytesRcvd = 0;
                do  /* while (bytesRcvd && (totalBytesRcvd < echoStringLen)) */
                {
                    /* Receive up to the buffer size (minus 1 to leave space for 
                       a null terminator) bytes from the sender */
                    bytesRcvd = recv(sock, &echoBuffer[totalBytesRcvd], 
                                        (RCVBUFSIZE - 1 - totalBytesRcvd), 0);
                    if (bytesRcvd < 0)
                        DieWithError("recv() failed");
                    else if (!bytesRcvd)
                        ReportError("recv() connection closed prematurely");
                    else
                    {
                        totalBytesRcvd += bytesRcvd;   /* Keep tally of total bytes */
                        echoBuffer[totalBytesRcvd] = '\0';  /* Add \0 so printf knows where to stop */
                    }
                } while (bytesRcvd && (totalBytesRcvd < echoStringLen));
    
                /* If message received back, print it */
                if (bytesRcvd)
                    printf("Received: %s\n",echoBuffer);    
                    
            }   /* end if (echoStringLen > 1) */
        } while (bytesRcvd && (echoStringLen > 1)); /* repeat loop if server  */
                                                    /*   hasn't disconnected  */
                                                    /*   and user hasn't quit */
      
    /* 6. Shut down (shutdown()) the connection. */
        /* Quitting, so shut down the connection */
        shutdown(sock,1);   /* "done sending" */
    
        /* watch for signal that server has shut down this socket */
        /* We will be notified of server shutdown by getting a    */
        /*     return value of zero from recv(), so continue      */
        /*     reading from the socket until it returns a zero.   */
        totalBytesRcvd = 0;
        while (bytesRcvd > 0)
        {
            bytesRcvd = recv(sock, echoBuffer, RCVBUFSIZE - 1, 0);
            if (bytesRcvd < 0)
                DieWithError("recv() failed");
            else if (!bytesRcvd)
                printf("Detected Server shutting down\n");
            else
            {
                /* unlikely, but if we're still receiving data, then process it */
                totalBytesRcvd += bytesRcvd;   /* Keep tally of total bytes */
                echoBuffer[totalBytesRcvd] = '\0';  /* Add \0 so printf knows where to stop */
            }
        }
    
        /* In the unlikely event that any data was received, then print it */
        if (totalBytesRcvd)
            printf("%s\n",echoBuffer); 
        
    /* 7. Close the socket. */
        /* Close the socket   */
    #ifdef WINSOCK_EXAMPLE
        /* Winsock requires a special function for sockets */
        closesocket(sock);
    #else
        close(sock);
    #endif    
    }
    
    
    /********************************************************/
    /* DieWithError                                         */
    /*    Separate function for handling errors             */
    /*    Reports an error and then terminates the program  */
    /********************************************************/
    void DieWithError(char *errorMessage)
    {
        ReportError(errorMessage);
        exit(1);
    }
    
    /**************************************************************************/
    /* ReportError                                                            */
    /*    Displays a message that reports the error                           */
    /*    Encapsulates the difference between UNIX and Winsock error handling */
    /* Winsock Note:                                                          */
    /*    WSAGetLastError() only returns the error code number without        */
    /*    explaining what it means.  A list of the Winsock error codes        */
    /*    is available from various sources, including Microsoft's            */
    /*    on-line developer's network library at                              */
    /*  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp */
    /**************************************************************************/
    void ReportError(char *errorMessage)
    {
    #ifdef WINSOCK_EXAMPLE
        fprintf(stderr,"%s: %d\n", errorMessage, WSAGetLastError());
    #else
        perror(errorMessage);
    #endif    
    }
    
    
    /***********************************************************************/
    /* chomp -- removes newline characters from the end of a string        */
    /*      Unlike the Perl function it's derived from, this chomp does    */
    /*          not return the number of characters removed, nor does it   */
    /*          restrict itself to the system's specific end-of-line       */
    /*          sequence ($/).                                             */
    /*      This function considers any number or combination of the       */
    /*          characters CR ('\x0d') and LF ('\x0a') to be part of       */
    /*          an end-of-line sequence and so removes them.               */
    /***********************************************************************/
    void chomp(char *str)  
    {
        int i, len;
        
        len = strlen(str);
        for (i=len-1; (i>=0) && ( (str[i]=='\x0a') || (str[i]=='\x0d') ); i--)
            str[i] = '\0';
    }
    
    /* End of File */
    And if you were to peruse the header files in your compiler's INCLUDE directory, you will undoubtedly see many conditional compilation directives selecting what code to use based on environmental conditions, such as the type of operating system, the OS' version, etc.
  4. #3
  5. Banned ;)
    Devshed Supreme Being (6500+ posts)

    Join Date
    Nov 2001
    Location
    Woodland Hills, Los Angeles County, California, USA
    Posts
    9,607
    Rep Power
    4247
    One more reason is for conditional compilation. For example, let's say I write a module called common.h
    Code:
    /* common.h */
    #include <stdio.h>
    
    #define SOMEVALUE 3
    
    typedef struct {
        int a;
        char b;
    } Some_Struct_t;
    
    int write_something(FILE *fp, int value);
    Note that common.h itself includes <stdio.h> because it uses the FILE definition from there. It is normal for include files to include other include files within them. Now let's say I use common.h in multiple .c files:
    Code:
    file1.c
    --------
    #include "common.h"
    ....
    
    file2.c
    --------
    #include "somefile.h"
    #include "common.h"
    ...
    ...
    Now, the C preprocessor replaces all #include statements with the actual file contents before passing it on to the compiler. Now, as I mentioned earlier, it is common for include files to include other files within them. Let's say that due to some dependencies, the file "somefile.h" also #includes "common.h" within it. Therefore, the C preprocessor will put two copies of common.h when it attempts to compile file2.c and the compiler will complain about multiple redefinitions. So how do we handle this then?

    One way to do this is to use conditional compilation. We can write common.h like this:
    Code:
    /* common.h */
    #ifndef COMMON_H_20131213
    #define COMMON_H_20131213
    
    #include <stdio.h>
    
    #define SOMEVALUE 3
    
    typedef struct {
        int a;
        char b;
    } Some_Struct_t;
    
    int write_something(FILE *fp, int value);
    
    #endif
    Now that we've put guard defines around the .h file contents, it does not matter if it is #included multiple times in a file, because the guard defines will ensure that only one copy gets processed. The way it works is:
    #ifndef SOME_LONG_UNIQUE_STRING
    the first time the preprocessor sees this, SOME_LONG_UNIQUE_STRING is not defined, so it will go inside the block.
    The very next statement is:
    #define SOME_LONG_UNIQUE_STRING
    which defines that symbol. The preprocessor then continues to include the various lines till it hits the #endif.

    Now, the second time the .h file is included, the preprocessor will see the
    #ifndef SOME_LONG_UNIQUE_STRING
    again. However, the symbol is already defined previously, so this time the preprocessor will skip the entire block upto the #endif and therefore only one copy of the .h file will be included before getting passed to the compiler. This solves the problem.
    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
  6. #4
  7. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Dec 2013
    Posts
    2
    Rep Power
    0
    Originally Posted by Scorpions4ever
    One more reason is for conditional compilation. For example, let's say I write a module called common.h
    Code:
    /* common.h */
    #include <stdio.h>
    
    #define SOMEVALUE 3
    
    typedef struct {
        int a;
        char b;
    } Some_Struct_t;
    
    int write_something(FILE *fp, int value);
    Note that common.h itself includes <stdio.h> because it uses the FILE definition from there. It is normal for include files to include other include files within them. Now let's say I use common.h in multiple .c files:
    Code:
    file1.c
    --------
    #include "common.h"
    ....
    
    file2.c
    --------
    #include "somefile.h"
    #include "common.h"
    ...
    ...
    Now, the C preprocessor replaces all #include statements with the actual file contents before passing it on to the compiler. Now, as I mentioned earlier, it is common for include files to include other files within them. Let's say that due to some dependencies, the file "somefile.h" also #includes "common.h" within it. Therefore, the C preprocessor will put two copies of common.h when it attempts to compile file2.c and the compiler will complain about multiple redefinitions. So how do we handle this then?

    One way to do this is to use conditional compilation. We can write common.h like this:
    Code:
    /* common.h */
    #ifndef COMMON_H_20131213
    #define COMMON_H_20131213
    
    #include <stdio.h>
    
    #define SOMEVALUE 3
    
    typedef struct {
        int a;
        char b;
    } Some_Struct_t;
    
    int write_something(FILE *fp, int value);
    
    #endif
    Now that we've put guard defines around the .h file contents, it does not matter if it is #included multiple times in a file, because the guard defines will ensure that only one copy gets processed. The way it works is:
    #ifndef SOME_LONG_UNIQUE_STRING
    the first time the preprocessor sees this, SOME_LONG_UNIQUE_STRING is not defined, so it will go inside the block.
    The very next statement is:
    #define SOME_LONG_UNIQUE_STRING
    which defines that symbol. The preprocessor then continues to include the various lines till it hits the #endif.

    Now, the second time the .h file is included, the preprocessor will see the
    #ifndef SOME_LONG_UNIQUE_STRING
    again. However, the symbol is already defined previously, so this time the preprocessor will skip the entire block upto the #endif and therefore only one copy of the .h file will be included before getting passed to the compiler. This solves the problem.
    thanks for your answer,
    if it is not forbidden, can you share name of your website with me, i am interested in socket programming too.
    greetings
  8. #5
  9. Contributing User

    Join Date
    Aug 2003
    Location
    UK
    Posts
    5,109
    Rep Power
    1802
    Another situation in which you might use conditional compilation, regardless of size and complexity of code is to test against predefined macros, in this case tge macro definition is determind by teh compiler and or the compiler options used, so in that case you do not "already know" whether it is defined or not.

    Examples:

    NDEBUG is defined when the code is built without debug information. You can use this to switch off debug code in release builds:
    C Code:
    #if defined NDEBUG
      #define DEBUG( fmt, ... )
    #else
      #define DEBUG( fmt, ... ) printf( "Debug: " fmt "\n", __VA_ARGS__ )
    #endif


    Compilers that target specific operating systems, normally have predefined macros; you can use these to write code that will work on different operating systems:
    C Code:
    #if defined _WIN32
      #include "windows.h"
      #define sleep_seconds( s ) Sleep( (s) * 1000 )
    #else if defined _POSIX_
      #include <unistd.h> 
      #define sleep_seconds(s ) sleep( s )
    #else
      #error sleep_seconds() not defined
    #endif
  10. #6
  11. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,145
    Rep Power
    2222
    Originally Posted by deniz
    thanks for your answer,
    if it is not forbidden, can you share name of your website with me, i am interested in socket programming too.
    greetings
    http://pgm.dwise1.net/sockets/index.html

IMN logo majestic logo threadwatch logo seochat tools logo