C Language – An Integrated Example (The Five Grammatical Elements of a Programming Language)

1. Integrated Example

The following example is a comprehensive example that uses all five grammar elements of the C programming language (variables, operators, statements, control, and functions) and includes all 32 reserved keywords and 12 preprocessor directives under the C90 standard in a single compilable source file.

exam.c
/*
 * exam.c - Comprehensive C grammar example
 *
 */

// #include - include C headers
#include <stdio.h>
#include <stdlib.h>

// #define - define macro
#define MAX_COUNT       5
#define BUF_SIZE        64
#define DEBUG_LEVEL     0

// #undef - undefine macro
#undef  DEBUG_LEVEL
#define DEBUG_LEVEL     1

// #ifndef / #endif - compile only if macro is not defined
#ifndef RELEASE
#define RELEASE
#endif

// #if / #elif / #else / #endif - conditional compilation
#if   DEBUG_LEVEL == 0
    #define BUILD_TYPE "Release"
#elif DEBUG_LEVEL == 1
    #define BUILD_TYPE "Debug"
#else
    #define BUILD_TYPE "Unknown"
#endif

// #ifdef / #error / #endif - trigger a compile error when a specific macro is defined
#ifdef DEBUG_MODE
#error "DEBUG_MODE must not be defined in production builds"
#endif

// #line  - reset compiler line number to 100
#line 100

// #pragma - compiler directive
// The following handles warnings to ignore intentionally unused variables
#pragma GCC diagnostic ignored "-Wunused-variable"

/* ------------------------------------------------------------------ */
/*  Type definitions  (typedef struct enum union)                     */
/* ------------------------------------------------------------------ */

// typedef + struct
typedef struct {
    int x;
    int y;
} Point;

// typedef + union
typedef union {
    int   i;
    float f;
} Number;

// typedef + enum
typedef enum {
    STATUS_IDLE    = 0,
    STATUS_RUNNING = 1,
    STATUS_DONE    = 2
} Status;

/* ------------------------------------------------------------------ */
/*  File-scope storage class keywords                                 */
/* ------------------------------------------------------------------ */

// extern - indicates that a function in another .c file is used here
extern void external_log(const char *msg);

// static  - internal linkage; value persists across calls
static int call_count = 0;

// volatile - value may change outside normal program flow
volatile int interrupt_flag = 0;

/* ------------------------------------------------------------------ */
/* Function prototypes                                                */
/* ------------------------------------------------------------------ */
int    increase(int value);
void   print_info(const char *label, int value);
double compute_average(int *arr, unsigned int size);
void   demonstrate_types(void);

/* main function */
int main(void)
{
    // auto - automatic (local) storage duration
    auto int count = 0;

    // const - read-only variable
    const int LIMIT = 3;

    // register - hint to store in CPU register
    register int i;

    // basic type keywords
    short         s     = 10;
    long          l     = 100000L;
    unsigned int  u     = 255u;
    signed char   sc    = -1;
    char          grade = 'A';
    float         ratio = 0.75f;
    double        pi    = 3.14159265358979;

    // composite types
    Point  p      = {10, 20};
    Number num;
    Status status = STATUS_IDLE;
    int    arr[MAX_COUNT];

    // static local variable
    static int run_count = 0;

    // intentionally unused variable to demonstrate #pragma behavior
    int unused_var;

    // operations: arithmetic, comparison, logical
    count  = count + 1;          // + (arithmetic)
    count  = count - 1;          // - (arithmetic)
    u      = u / 5;              // / (arithmetic)
    s      = (short)(s * 2);     // * (arithmetic)
    num.i  = (int)pi % 3;        // % (arithmetic)
    run_count++;
    call_count++;

    // sizeof operator
    printf("sizeof(int)    = %u bytes\n", (unsigned)sizeof(int));
    printf("sizeof(double) = %u bytes\n", (unsigned)sizeof(double));
    printf("sizeof(Point)  = %u bytes\n", (unsigned)sizeof(Point));

    // control: while
    while (count < MAX_COUNT) {
        if (count == LIMIT) {    // if
            break;               // break
        } else {                 // else
            count = increase(count);
        }
    }

    // control: do-while + continue
    do {
        call_count++;
        if (call_count % 2 == 0) {
            continue;            // continue
        }
        print_info("call_count", call_count);
    } while (call_count < 4);   // while

    // control: for
    for (i = 0; i < MAX_COUNT; i++) {
        arr[i] = i + 1;
    }

    // control: switch / case / default
    switch (status) {
        case STATUS_IDLE:
            status = STATUS_RUNNING;
            break;
        case STATUS_RUNNING:
            status = STATUS_DONE;
            break;
        default:
            break;
    }

    // control: goto
    if (interrupt_flag != 0) {
        /*
            Since `goto` makes program flow harder to trace,
            it should be avoided whenever possible except for exceptional
            error-handling sections.
        */
        goto error_exit;
    }

    // statements: function calls, input/output
    demonstrate_types();

    print_info("count",  count);
    print_info("LIMIT",  LIMIT);
    print_info("status", (int)status);

    printf("build  = %s\n",      BUILD_TYPE);
    printf("grade  = %c\n",      grade);
    printf("ratio  = %.2f\n",    ratio);
    printf("pi     = %.5f\n",    pi);
    printf("short  = %d\n",      s);
    printf("long   = %ld\n",     l);
    printf("uint   = %u\n",      u);
    printf("schar  = %d\n",      (int)sc);
    printf("point  = (%d, %d)\n", p.x, p.y);
    printf("num.i  = %d\n",      num.i);

    {
        double avg = compute_average(arr, MAX_COUNT);
        printf("avg    = %.1f\n", avg);
    }

    return 0;   /* return */

error_exit:
    fprintf(stderr, "Error: interrupt detected.\n");
    return 1;
}

/* increase - increment value by 1 and return */
int increase(int value)
{
    return value + 1;
}

/* print_info - print string label and integer value */
void print_info(const char *label, int value)
{
    printf("%-6.6s = %d\n", label, value);
}

/* compute_average - calculate arithmetic mean of int array */
double compute_average(int *arr, unsigned int size)
{
    int sum = 0;
    unsigned int j;

    for (j = 0; j < size; j++) {
        sum += arr[j];
    }
    return (double)sum / size;
}

/* demonstrate_types - example usage of C90 basic types */
void demonstrate_types(void)
{
    signed   int   si =  -10;
    unsigned int   ui =   10;
    short    int   sh =    5;
    long     int   li = 1000L;
    char           c  =  'Z';
    float          f  =  1.5f;
    double         d  =  2.5;

    printf("[types] si=%d ui=%u sh=%d li=%ld c=%c f=%.1f d=%.1f\n",
           si, ui, sh, li, c, f, d);
}
exam_func.c
/*
 * Implementation file for the function declared as extern in exam.c
 */

#include <stdio.h>

/* extern function example */
void external_log(const char *msg)
{
    printf("[log] %s\n", msg);
}
  • Coverage: variables, operations, statements, control statements, functions
  • Components: C90 keywords, preprocessor directives, struct/union/enum, function call flow

1.1. Execution Result

$ gcc -Wall -std=c11 -o exam exam.c exam_func.c
$ ./exam
sizeof(int)    = 4 bytes
sizeof(double) = 8 bytes
sizeof(Point)  = 8 bytes
call_c = 3
[types] si=-10 ui=10 sh=5 li=1000 c=Z f=1.5 d=2.5
count  = 3
LIMIT  = 3
status = 1
build  = Debug
grade  = A
ratio  = 0.75
pi     = 3.14159
short  = 20
long   = 100000
uint   = 51
schar  = -1
point  = (10, 20)
num.i  = 0
avg    = 3.0

1.2. Control Flow

main() starts
  │
  ├─ while (count < MAX_COUNT)
  │    ├─ if (count == LIMIT)  → break  ← exits loop when count becomes 3
  │    └─ else                 → count = increase(count)
  │
  ├─ do-while (call_count < 4)
  │    ├─ if (call_count % 2 == 0) → continue  ← skips output on even values
  │    └─ print_info(...)
  │
  ├─ for (i = 0; i < MAX_COUNT; i++)
  │    └─ arr[i] = i + 1       ← initializes to {1, 2, 3, 4, 5}
  │
  ├─ switch (status)
  │    ├─ STATUS_IDLE    → STATUS_RUNNING
  │    ├─ STATUS_RUNNING → STATUS_DONE
  │    └─ default        → break
  │
  ├─ if (interrupt_flag != 0) → goto error_exit
  │
  ├─ output statements
  │
  └─ return 0

error_exit:
  └─ fprintf(stderr, ...) → return 1