Preprocessing, C Standard Library and High-Level Technology

Dr. Cheng-Chung Li
May 23, 2017

Preprocessing

  • In this chapter, we will learn pre-processings, which are processes that the compiler has to address before compiling your program.
  • The pre-process command starts from “#”, which we have seen many times in the course.
  • Note that this command is NOT a illustration in C program, so it does not end at ;

#include

  • Using #include can include the header file into your program.
  • As referred in 18.2, after including require header file, we can then use the corresponding data structures and functions (we know their prototype, i.e. name, and type signature).
  • Note that system headfer file is bracketed by <> but self-defined header file is bracketed by “”
#include <stdio.h>
#include "header.h"

Macro

A macro in computer science is a rule or pattern that specifies how a certain input sequence (often a sequence of characters) should be mapped to a replacement output sequence (also often a sequence of characters) according to a defined procedure.

#define

  • Substitutes a preprocessor macro
  • With #define, we can set and change some variables in a quicker way.

#define Example: Array Size in C Program

#include <stdio.h>
#define ARRAYSIZE 10

int main(void)
{
    int a[ARRAYSIZE];
    int i; 
    for (i=0; i< ARRAYSIZE; i++)
        scanf("%d", &(a[i]));
    for (i=0; i< ARRAYSIZE; i++)
        printf("%d", a[i]);
    return 0;
}

#define Example: Stack Size in Header File

stack-define.h

#define STACKSIZE 100
struct stack{
    int top; 
    char elements[STACKSIZE]; 
};
typedef struct stack Stack;
void init_stack(Stack *s);
int stack_full(Stack *s);
int stack_empty(Stack *s);
void push_stack(Stack *s, char data);
int pop_stack(Stack *s);

#define Example: Including Header File

#include "stack-define.h"
int stack_full(Stack *s){
    return (s->top >= STACKSIZE);
}

#define Example: Data Type in Header File

#define STACKSIZE 100
#define Data int
struct stack{
    int top; 
    Data elements[STACKSIZE]; 
};
typedef struct stack Stack;
void init_stack(Stack *s);
int stack_full(Stack *s);
int stack_empty(Stack *s);
void push_stack(Stack *s, Data data);
Data pop_stack(Stack *s);

#undef

Undefines a preprocessor macro.

#undef FILE_SIZE 
#define FILE_SIZE 42

#if

  • We can use #if to compile the source code under the specified condition.
  • Tests if a compile time condition is true.
#if condition 
source code 
#endif

#if Example: Compile printf

#define DEBUG 1
#if DEBUG == 1
    printf ("i = %d\n", i);
#endif

#if Example: Do not Compile printf

#define DEBUG 0
#if DEBUG == 1
    printf ("i = %d\n", i);
#endif

#if Example: Compile printf by DEBUG_LEVEL

#define DEBUG_LEVEL 4
...
#if DEBUG_LEVEL >= 3
    printf ("i = %d\n", i);
#endif
...
#if DEBUG_LEVEL >= 5
    printf ("j = %d\n", j);
#endif

Use gcc to Change Value

we can use “gcc -DDEBUG_LEVEL=8 program.c”“ in command line to set DEBUG_LEVEL as 8 in program.c

Redefine Problem

However, if we set the value in command line and in program, it may cause “redefine problem” since this value has been set twice.

#ifndef

  • Use #ifndef can avoid “redefine problem”. In the below example, if DEBUG_LEVEL is set in the command line, it does not re-set again. If not, it will set DEBUG_LEVEL as 4.
  • Returns true if this macro is not defined.
#ifndef DEBGU_LEVEL
#define DEBGU_LEVEL 4
#endif

#ifdef

  • We can use #ifdef to check if a value is defined. If yes, process the code; if no, do nothing.
  • Returns true if this macro is defined.
#ifdef DEBUG
source code 
#endif

Some Predefined Macros

  • __ DATE __ : The current date as a character literal in “MMM DD YYYY” format
  • __ TIME __ : The current time as a character literal in “HH:MM:SS” format
  • __ LINE __ : This contains the current line number as a decimal constant
  • __ FILE __ : This contains the current filename as a string literal

__DATE__ and __TIME__ Example

#include<stdio.h>

int main(void){
   printf("Date :%s\n", __DATE__ );
   printf("Time :%s\n", __TIME__ );
}

__LINE__ and __FILE__ Example

#include<stdio.h>
#include <stdlib.h>

#ifdef NDEBUG
#define assert(cond) 0
#else
#define assert(cond) \ 
if (!(cond)) { \
    printf("Assertion failed: %s, "\ 
    "file %s, line %d\n", \
    #cond, __FILE__, __LINE__); \
    exit(0); \
    }
#endif

__LINE__ and __FILE__ Example

int main(void){
    int i = 0; 
    assert(i==1);
    return 0;
}

__LINE__ and __FILE__ Example

  • A macro is normally confined to a single line. The macro continuation operator (\) is used to continue a macro that is too long for a single line.
  • The stringize or number-sign operator ('# ), when used within a macro definition, converts a macro parameter into a string constant.

Random Number

We can use

  • srand to set a seed
  • rand to generate a random number between 0 to RAND_MAX
void srand(unsigned int seed);
int rand(void);

Random Number Example: Set the same Random Number

#include<stdio.h>
#include<stdlib.h>

int main(void){
    int i, j; 

    srand(523); 
    i = rand(); 
    printf("%d\n", i); 

    srand(523);
    j = rand(); 
    printf("%d\n", j);
    return 0; 
}

Random Number Example: Set Different Random Number

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int main(void){
    srand(time(NULL));
    int i,j;
    i = rand(); 
    printf("%d\n", i); 

    j = rand(); 
    printf("%d\n", j);
    return 0; 
}

Random Number Example: Calculate PI by Monte Carlo Method

#include<stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
    int i, count =0, n; 
    double x, y ; 

    scanf("%d", &n);
    srand(time(NULL));

Random Number Example: Calculate PI by Monte Carlo method

    for (i=0; i<n; i++){
        x = (double) rand()/RAND_MAX; 
        y = (double) rand()/RAND_MAX; 

        if (x*x + y*y<=1.0){
            count ++; 
        }
    }
    printf("pi is about %f\n", (double) count/n * 4.0);
    return 0;
}

exit

  • When runnin the program, if something fatal happens, we can use exit to terminate the program immediately.
  • Note that exit is different from return since return is to give the control to where it is called.

exit Example: The Difference between exit and return

#include <stdio.h>
#include <stdlib.h>

void bar (double d){
    printf("running bar\n");
    if (d >0)
        return ;
    else 
        exit(-1);
}

exit Example: The Difference between exit and return

void foo(double d){
    bar (d);
    printf("running foo\n");
    return;
}
int main(void){
    double d = 1.0;
    foo(d);

    scanf("%lf", &d);
    foo(d);
}

Command Line Argument

Instead of inputing parameters from the keyboard or file, we can input them from the command line.

  • type “ls -l” to obtain the file list in detail, where “ls” is the command, and “-l” is command line argument.

Command Line Argument in main function

int main(int argc, char *[argv]);
  • argc: integer: the number of parameters, which includes the command itself.
  • argv: array, and its elements are charachter pointers.

Command Line Argument Example

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[]){
    int i;
    int sum = 0; 
    for (i= 1; i<argc; i++){
        sum += atoi(argv[i]);
    }
    printf ("%d\n", sum);
    return 0;
}

Command Line Argument Example

  • Note that the index of argv starts from 1, and argv[0] is the command itself.
  • atoi is a standard libray in C to transform string into integer. Please refer to section 20.2 in the textbook.