๐Ÿ”ข Prime Numbers โ€” Logic ยท Flowchart ยท Functions ยท Pointers ยท Arrays
0%
C Programming  ยท  Prime Numbers  ยท  Complete Guide

๐Ÿ”ข Prime Numbers in C โ€”
Logic, Flowchart & Three Approaches

What makes a number prime โ€” explained plainly. The logic visualised as a flowchart. Then built three ways: using a function, using a pointer, and using an array โ€” each adding a new concept while solving the same problem.

๐Ÿ“–
What is a Prime? Analogy + Logic
๐Ÿ“Š
Flowchart Step by Step
โš™๏ธ
Way 1 โ€” Function isPrime()
๐ŸŽฏ
Way 2 โ€” Pointer Version
๐Ÿ“ฆ
Way 3 โ€” Store Primes in Array (Sieve idea)
๐Ÿ“–
What is a Prime Number? โ€” Plain English First
Build the idea from scratch before writing a single line of code
Concept
Plain English Definition
A prime number is a whole number greater than 1 that can only be divided evenly by exactly two numbers: 1 and itself. That's it. Nothing else divides it without leaving a remainder.

7 is prime โ€” try dividing 7 by 2, 3, 4, 5, 6. Every time there is a remainder. Only 1 and 7 divide it cleanly.

6 is NOT prime โ€” 6 รท 2 = 3 exactly. A third number (2) divides it. So 6 has more than two divisors โ†’ not prime.

1 is NOT prime โ€” special case. 1 has only ONE divisor (itself). Prime requires exactly two. So 1 is excluded by definition.
Primes highlighted โ€” numbers 1 to 50
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Prime Not prime Special (1)
The key question C asks for every number n: "Does any number between 2 and n-1 divide n with no remainder?" If yes โ†’ not prime. If no โ†’ prime. In code, remainder is the % operator. n % i == 0 means i divides n exactly.

Smart shortcut โ€” only check up to โˆšn: If n has a divisor larger than โˆšn, it must also have one smaller than โˆšn. So once we have checked all numbers up to โˆšn without finding a divisor, we are done โ€” n is prime. For n=100, โˆš100=10, so we only need to check 2 through 10 โ€” not 2 through 99.
NumberDivisors checkedResultReason
2none (loop doesn't start)PRIME2 is the smallest prime โ€” no divisors between 2 and 1
92, 3NOT PRIME9 % 3 == 0 โ†’ divisible by 3
172, 3, 4 (โˆš17 โ‰ˆ 4.1)PRIMENone of 2,3,4 divide 17 โ€” so prime
1002 (โˆš100=10)NOT PRIME100 % 2 == 0 โ†’ stops immediately
972,3,4,5,6,7,8,9 (โˆš97โ‰ˆ9.8)PRIMENone divide 97 โ€” last prime before 100
The โˆšn trick saves huge time. For n = 1,000,000: naive check needs 1,000,000 divisions. With โˆšn, only 1,000 divisions. That is 1000ร— faster โ€” same answer, less work.
flowchart โ€” step by step logic
๐Ÿ“Š
Flowchart โ€” isPrime Logic Visualised
Every decision, every loop, every exit path drawn as a proper flowchart
Flowchart
The flowchart has two nested decisions. The outer loop walks every number from 2 to n. For each number, the inner function checks divisibility from 2 up to โˆšnum. Two exit paths exist from the inner check: finding a divisor (not prime) or exhausting all checks without finding one (prime). The connector circle A routes prime numbers to the print step without re-entering the inner check.
Complete isPrime flowchart โ€” outer loop + inner divisibility check
START n = 100 ; num = 2 num <= n ? NO โ†’ END YES i = 2 ; isPrime = 1 i*i <= num ? (i โ‰ค โˆšnum) YES num % i == 0 ? isPrime = 0 break inner YES A NO i++ inner loop A isPrime == 1? YES print num NO num++ outer loop back END
way 1 โ€” function isPrime()
โš™๏ธ
Way 1 โ€” isPrime() Function โ€” Clean, Reusable, Readable
Separate function returns 1 (prime) or 0 (not) โ€” main just calls it in a loop
Function
The cleanest approach: pull the prime-checking logic into a separate function isPrime(int n) that returns 1 if n is prime and 0 if not. The main function simply loops from 2 to 100 and calls isPrime for each number. This follows the single responsibility principle โ€” one function does one thing. The function is also reusable: you can call it from anywhere, not just from main.
way1_function.c
C
#include <stdio.h>

/* Returns 1 if n is prime, 0 otherwise */
int isPrime(int n) {
    if (n < 2) return 0;        /* 0 and 1 are NOT prime         */
    if (n == 2) return 1;       /* 2 is the only even prime      */
    if (n % 2 == 0) return 0;  /* all other even โ†’ not prime    */

    /* Check odd divisors from 3 up to โˆšn */
    for (int i = 3; i * i <= n; i += 2) {
        if (n % i == 0)
            return 0;           /* divisor found โ†’ not prime     */
    }
    return 1;                    /* survived all checks โ†’ prime   */
}

int main() {
    int n = 100;
    int count = 0;

    printf("Prime numbers from 1 to %d:\n", n);
    printf("--------------------------------\n");

    for (int num = 2; num <= n; num++) {
        if (isPrime(num)) {         /* call function โ€” clean! */
            printf("%4d", num);
            count++;
            if (count % 10 == 0) printf("\n"); /* 10 per line */
        }
    }

    printf("\n--------------------------------\n");
    printf("Total primes found: %d\n", count);
    return 0;
}
output
Prime numbers from 1 to 100:
--------------------------------
   2   3   5   7  11  13  17  19  23  29
  31  37  41  43  47  53  59  61  67  71
  73  79  83  89  97
--------------------------------
Total primes found: 25
Three quick optimisations inside isPrime: (1) Return 0 immediately for n < 2. (2) Return 1 immediately for n == 2 (only even prime). (3) Return 0 for all other even numbers โ€” halves the remaining checks. Then step by 2 (i += 2) โ€” only check odd divisors. Together these make the function roughly 4ร— faster than the naive version.
way 2 โ€” pointer version
๐ŸŽฏ
Way 2 โ€” Pointer Version โ€” Pass by Pointer, Walk by Pointer
checkPrime(int *n, int *result) โ€” function writes answer through pointer โ€” pointer walk prints
Pointers
The pointer version demonstrates two pointer patterns at once. Pattern 1 โ€” output pointer: checkPrime(int *n, int *result) receives the number to test via pointer and writes the answer (1 or 0) into *result โ€” communicating the result back to the caller without a return value. Pattern 2 โ€” pointer walk: to print primes in an array, we use a pointer that walks the array instead of an index. Both patterns appear in real C codebases constantly.
way2_pointers.c
C
#include <stdio.h>
#include <math.h>

/* Pointer version โ€” writes result through *result pointer */
void checkPrime(const int *n, int *result) {
    int num = *n;                /* read value through pointer   */
    *result = 1;               /* assume prime via pointer      */

    if (num < 2)  { *result = 0; return; }
    if (num == 2) { *result = 1; return; }
    if (num % 2 == 0) { *result = 0; return; }

    for (int i = 3; i * i <= num; i += 2) {
        if (num % i == 0) {
            *result = 0;        /* write NOT PRIME through ptr  */
            return;
        }
    }
    /* *result stays 1 โ€” prime */
}

/* Print array via pointer walk โ€” no index used */
void printViaPtrWalk(const int *arr, int count) {
    const int *p   = arr;          /* pointer starts at arr[0]     */
    const int *end = arr + count;  /* one-past-last sentinel        */
    int  col = 0;

    while (p < end) {
        printf("%4d", *p);         /* dereference pointer           */
        p++;                        /* advance to next element       */
        if (++col % 10 == 0) printf("\n");
    }
}

int main() {
    int  n       = 100;
    int  storage[30];  /* there are 25 primes โ‰ค 100    */
    int  count   = 0;
    int  result  = 0;

    for (int num = 2; num <= n; num++) {
        checkPrime(&num, &result);  /* pass ADDRESSES โ€” pointer API  */
        if (result)
            storage[count++] = num; /* save prime in array           */
    }

    printf("Primes 1-%d via pointer walk:\n", n);
    printf("--------------------------------\n");
    printViaPtrWalk(storage, count);

    printf("\n--------------------------------\n");
    printf("Count : %d\n", count);
    printf("Largest: %d\n", storage[count-1]);
    printf("Smallest: %d\n", storage[0]);

    /* Pointer arithmetic โ€” sum of primes */
    int sum = 0;
    const int *p = storage;
    const int *e = storage + count;
    while(p < e) sum += *p++;
    printf("Sum    : %d\n", sum);
    return 0;
}
output
Primes 1-100 via pointer walk:
--------------------------------
   2   3   5   7  11  13  17  19  23  29
  31  37  41  43  47  53  59  61  67  71
  73  79  83  89  97
--------------------------------
Count : 25
Largest: 97
Smallest: 2
Sum    : 1060
Why pass &num as const int *n? The function does not need to modify the number โ€” so const documents that promise. But passing by pointer rather than value demonstrates the output-pointer pattern: *result is how the function communicates its answer back. This is exactly how scanf works โ€” you pass &variable and it writes the answer through the pointer.
way 3 โ€” array (sieve idea)
๐Ÿ“ฆ
Way 3 โ€” Array Version โ€” Sieve of Eratosthenes
Boolean array[101] marks composites โ€” remaining unmarked = primes โ€” O(n log log n)
Array + Sieve
The Sieve of Eratosthenes is the smartest approach โ€” it uses an array of booleans. Start by marking every number as prime (1). Then for each prime p starting at 2, mark all its multiples as not prime (0). When done, every index still marked 1 is a prime. The name comes from ancient Greece: imagine crossing out multiples on a number grid like a sieve โ€” primes are what remains.

Key insight: for each prime p, start crossing out from pร—p โ€” everything before that was already crossed out by smaller primes. This gives O(n log log n) time โ€” much faster than checking each number individually.
Sieve idea visualised โ€” mark[i] = 0 means i is NOT prime
Initial After p=2 After p=3 Final 2 3 4 5 6 7 8 9 10 11 12 13 2 3 4 5 6 7 8 9 10 11 12 13 2 3 4 5 6 7 8 9 10 11 12 13 2โœ“ 3โœ“ 4 5โœ“ 6 7โœ“ 8 9 10 11โœ“ 12 13โœ“
way3_sieve_array.c
C โ€” Sieve of Eratosthenes
#include <stdio.h>
#include <string.h>

#define N 100

/* Build the sieve โ€” marks composite numbers as 0 */
void buildSieve(int *mark, int n) {
    /* Step 1 โ€” fill array with 1 (assume all prime) */
    for(int i = 0; i <= n; i++) mark[i] = 1;
    mark[0] = mark[1] = 0;      /* 0 and 1 not prime             */

    /* Step 2 โ€” sieve: for each prime p, cross its multiples */
    for (int p = 2; p * p <= n; p++) {
        if (mark[p] == 1) {          /* p is still prime              */
            /* cross out p*p, p*p+p, p*p+2p ... */
            for (int j = p * p; j <= n; j += p)
                mark[j] = 0;          /* j is a multiple of p          */
        }
    }
}

/* Print primes using pointer walk โ€” index via ptr arithmetic */
int printPrimesPtr(const int *mark, int n) {
    const int *p   = mark + 2;   /* start from index 2           */
    const int *end = mark + n + 1;/* one past mark[n]             */
    int  count = 0, col = 0;

    printf("Primes via sieve (1 to %d):\n", n);
    printf("-------------------------------\n");

    while (p < end) {
        if (*p == 1) {                /* marked prime                 */
            printf("%4d", (int)(p - mark)); /* index = number       */
            count++; col++;
            if (col % 10 == 0) printf("\n");
        }
        p++;                           /* advance pointer              */
    }
    return count;
}

/* Stats via pointer scan */
void sieveStats(const int *mark, int n) {
    const int *p = mark + 2;
    const int *e = mark + n + 1;
    int sum=0, first=-1, last=-1, cnt=0;
    while(p < e){
        if(*p){
            int num = (int)(p - mark);
            if(first == -1) first = num;
            last = num;
            sum += num; cnt++;
        }
        p++;
    }
    printf("\n-------------------------------\n");
    printf("Count   : %d\n",   cnt);
    printf("Smallest: %d\n",   first);
    printf("Largest : %d\n",   last);
    printf("Sum     : %d\n",   sum);
    printf("Average : %.2f\n", (float)sum/cnt);
}

int main() {
    int mark[N + 1];

    buildSieve(mark, N);       /* pass array as pointer */
    printPrimesPtr(mark, N);   /* pointer walk + ptr arithmetic */
    sieveStats(mark, N);
    return 0;
}
output
Primes via sieve (1 to 100):
-------------------------------
   2   3   5   7  11  13  17  19  23  29
  31  37  41  43  47  53  59  61  67  71
  73  79  83  89  97
-------------------------------
Count   : 25
Smallest: 2
Largest : 97
Sum     : 1060
Average : 42.40
Pointer arithmetic for index: (int)(p - mark) โ€” p is a pointer into the mark array and mark is a pointer to element 0. Subtracting them gives the offset โ€” which is exactly the number being checked. No separate index variable needed โ€” the pointer position is the index.
ApproachTimeSpaceConcept taughtBest for
Way 1 โ€” FunctionO(nโˆšn)O(1)Function, return value, early returnChecking one number or small n
Way 2 โ€” PointerO(nโˆšn)O(n)Output pointer, pointer walk, ptr arithmeticTeaching pointer patterns
Way 3 โ€” SieveO(n log log n)O(n)Boolean array, sieve algorithm, ptr indexAll primes up to large n โ€” fastest
checklist โ€” tick when understood
  • Definition: Prime = whole number > 1 divisible only by 1 and itself. 1 is NOT prime (one divisor, not two). 2 is the only even prime. Check with n % i == 0.
  • โˆšn shortcut: Only check divisors up to โˆšn. Use i*i <= n to avoid sqrt(). For n=100 checks only up to 10. Roughly โˆšn times faster than checking up to n.
  • Flowchart logic: Outer loop num=2 to N. Inner loop i=2 while iร—iโ‰คnum. If num%i==0 โ†’ isPrime=0, break. After inner loop: if isPrime still 1 โ†’ print. num++, repeat.
  • Way 1 โ€” Function: isPrime(int n) returns 1 or 0. Handle edge cases first. Check n==2 (only even prime). Loop odd divisors i+=2. return 0 on first divisor found, return 1 after loop.
  • Way 2 โ€” Pointer: checkPrime(const int *n, int *result) reads via *n, writes answer via *result. Caller passes &num and &result. Pointer walk print: while(p < end) printf(*p++);
  • Way 3 โ€” Sieve: int mark[N+1] all 1s. mark[0]=mark[1]=0. For each prime p: cross multiples from pร—p. Pointer walk: p - mark gives index = number. Fastest for finding all primes to N.