๐ŸŽฏ Uncommon Pointer & Array Examples
0%
C Programming  ยท  Pointers & Arrays  ยท  Uncommon Examples

Uncommon Pointer
& Array Examples

Five examples you don't usually see โ€” each short, each surprising. The last one shows passing an array of 5 pointers to a function and printing through them. No structs, no complex programs โ€” just the pointer mechanics exposed plainly.

E1
Array name is a pointer
E2
Pointer doing array subscript
E3
Pointer to the middle of array
E4
Double pointer โ€” pointer to pointer
E5
Array of 5 pointers โ†’ function
E1
Array name is already a pointer โ€” no & needed
arr and &arr[0] are the same address โ€” you can use arr like a pointer directly
Array = Pointer
Most people write int *p = &arr[0] to get a pointer to the first element. But the array name itself is already that pointer. Writing int *p = arr is identical. This means arr[2] and *(arr+2) and *(p+2) and p[2] all produce the exact same value โ€” four different syntaxes for one operation.
arr in memory โ€” arr == &arr[0] == p
address โ†’ 1000 1004 1008 1012 1016 10 20 30 40 50 arr[0] arr[1] arr[2] arr[3] arr[4] โ†‘ arr == p == &arr[0] == 1000 int *p = arr stores: 1000
e1_array_is_pointer.c
C
#include <stdio.h>

int main() {
    int  arr[] = {10, 20, 30, 40, 50};
    int *p = arr;   /* same as &arr[0] โ€” no & needed */

    /* All four lines print the SAME value: 30 */
    printf("arr[2]      = %d\n",  arr[2]);    /* normal index     */
    printf("*(arr+2)    = %d\n",  *(arr+2)); /* pointer math     */
    printf("*(p+2)      = %d\n",  *(p+2));  /* via pointer      */
    printf("p[2]        = %d\n",   p[2]);   /* pointer as array */

    /* Proof: same address */
    printf("\narr    addr = %p\n", (void*)arr);
    printf("&arr[0] addr = %p\n", (void*)&arr[0]);
    printf("p      addr = %p\n", (void*)p);
    return 0;
}
output
arr[2]      = 30
*(arr+2)    = 30
*(p+2)      = 30
p[2]        = 30

arr    addr = 0x7ffd1a2b3c40
&arr[0] addr = 0x7ffd1a2b3c40
p      addr = 0x7ffd1a2b3c40
๐Ÿ’ก
Uncommon fact: you can use subscript [ ] on any pointer, not just arrays. p[2] is literally defined as *(p+2) in C. Even this works: 2[p] โ€” because 2[p] = *(2+p) = *(p+2). Addition is commutative.
e2 โ€” pointer doing subscript
E2
Pointer subscript โ€” walking a string with p[i] instead of p++
A char* can be subscripted like an array โ€” and the index can even go negative
Pointer [ ] Access
A char* pointer can use [ ] just like an array โ€” because they are the same operation. The uncommon trick: if you advance the pointer first (p++ or p += k), then p[-1] gives the character before the current position. Negative subscripts are legal in C as long as the address remains within the original array's bounds.
e2_negative_subscript.c
C
#include <stdio.h>

int main() {
    char word[] = "Ananta";
    char *p = word;         /* p โ†’ 'A'                     */

    p += 3;                 /* advance: p โ†’ 'n' (index 3)  */

    printf("p[0]  = %c\n",  p[0]);   /* 'n'  โ€” current position     */
    printf("p[1]  = %c\n",  p[1]);   /* 't'  โ€” one ahead            */
    printf("p[-1] = %c\n",  p[-1]); /* 'a'  โ€” one BEHIND (legal!)  */
    printf("p[-3] = %c\n",  p[-3]); /* 'A'  โ€” back to start        */

    /* Reverse print using negative subscript */
    int len = 6;
    p = word + len - 1;    /* p โ†’ last char 'a'           */
    printf("Reversed: ");
    for(int i = 0; i < len; i++)
        printf("%c", p[-i]); /* p[0] p[-1] p[-2] ...        */
    printf("\n");
    return 0;
}
output
p[0]  = n
p[1]  = t
p[-1] = a
p[-3] = A
Reversed: atnanA
๐Ÿ’ก
Negative subscript is legal as long as the computed address stays inside the original array. p[-1] just means *(p-1) โ€” one step before wherever p currently points. This is used in audio DSP buffers, circular queues, and boundary checks.
e3 โ€” pointer to the middle
E3
Pointer to the middle โ€” split one array into two views
Two pointers, same array โ€” left half and right half โ€” no copying needed
Mid-Array Pointer
You can point into the middle of an existing array and treat it as a separate array โ€” no copy, no malloc. int *right = arr + 5 means right[0] is arr[5], right[1] is arr[6], and so on. This is how C handles substrings, sub-arrays, and efficient partition algorithms โ€” by handing a pointer to the relevant position instead of copying data.
left points to arr[0], right points to arr[5] โ€” same memory, two views
10 20 30 40 50 60 70 80 90 100 [0] [5] left right left half โ€” left[0..4] right half โ€” right[0..4]
e3_mid_pointer.c
C
#include <stdio.h>

/* Print n elements via pointer โ€” reusable */
void printN(const int *p, int n, const char *label) {
    printf("%-8s: [ ", label);
    for(int i = 0; i < n; i++) printf("%d ", p[i]);
    printf("]\n");
}

int main() {
    int  arr[] = {10,20,30,40,50,60,70,80,90,100};

    int *left  = arr;      /* view of first half   */
    int *right = arr + 5; /* view of second half  */

    printN(left,  5, "left");   /* 10 20 30 40 50  */
    printN(right, 5, "right");  /* 60 70 80 90 100 */

    /* right[0] IS arr[5] โ€” same memory */
    right[0] = 999;
    printf("arr[5] after right[0]=999: %d\n", arr[5]); /* 999 */

    /* Distance between two pointers */
    printf("right - left = %td elements\n", right - left); /* 5 */
    return 0;
}
output
left    : [ 10 20 30 40 50 ]
right   : [ 60 70 80 90 100 ]
arr[5] after right[0]=999: 999
right - left = 5 elements
๐Ÿ’ก
No copy was made. left and right both point into the same array. Writing through right[0] changes arr[5]. This is how qsort, binary search, and merge sort work โ€” they pass sub-array pointers, not copies.
e4 โ€” pointer to pointer
E4
Pointer to a pointer โ€” int **pp โ€” two levels of indirection
pp stores the address of p, which stores the address of x โ€” reach x through two arrows
Double Pointer
int **pp is a pointer to a pointer to int. It stores the address of another pointer. To get to the actual integer you dereference twice: **pp. The uncommon use: you can change which variable p points to from inside a function, by passing &p as int **pp. Single pointer can change the value โ€” double pointer can change the pointer itself.
three variables, two levels of indirection
int **pp stores addr of p e.g. 2000 int *p stores addr of x e.g. 3000 int x = 42 the actual data addr: 3000 *pp โ†’ p **pp โ†’ x *p โ†’ x value: 42
e4_double_pointer.c
C
#include <stdio.h>

/* Changes which variable ptr POINTS AT โ€” needs int** */
void redirectPtr(int **pp, int *newTarget) {
    *pp = newTarget;   /* change what p points to */
}

int main() {
    int  x  = 42;
    int  y  = 99;
    int *p  = &x;      /* p โ†’ x                   */
    int**pp  = &p;     /* pp โ†’ p                  */

    printf("*p   = %d\n",   *p);   /* 42 โ€” through p         */
    printf("**pp = %d\n",  **pp);  /* 42 โ€” through pp then p */

    /* Change value through double pointer */
    **pp = 100;
    printf("x after **pp=100: %d\n", x);  /* 100 */

    /* Re-point p at y โ€” using double pointer */
    redirectPtr(&p, &y);
    printf("*p after redirect: %d\n", *p);  /* 99 */
    return 0;
}
output
*p   = 42
**pp = 42
x after **pp=100: 100
*p after redirect: 99
๐Ÿ’ก
Single pointer modifies the value. Double pointer modifies the pointer. If a function receives int *p and does p = &y, the caller's pointer is unchanged. But with int **pp and *pp = &y, the caller's pointer is redirected. This is why scanf takes &variable โ€” it needs a pointer to write through.
e5 โ€” array of 5 pointers passed to a function
E5
Array of 5 pointers โ€” passed to a function โ€” printed inside
int *arr[5] holds 5 addresses โ€” function receives int **arr โ€” dereferences each
int *arr[5] โ†’ Function
int *arr[5] is an array where each of the 5 slots holds a pointer to a different integer. When you pass this array to a function, the function receives it as int **arr โ€” a pointer to the first element, where each element is itself a pointer. Inside the function, arr[i] is the i-th pointer, and *arr[i] is the integer it points at. Two levels: array slot โ†’ pointer โ†’ integer.
int *arr[5] โ€” 5 slots, each holding a pointer to a different int
arr[ ] โ€” 5 pointer slots arr[0]โ†’ arr[1]โ†’ arr[2]โ†’ arr[3]โ†’ arr[4]โ†’ 10 20 30 40 50 a b c d e *arr[i] reaches these values
Declaration side
int *arr[5]
โ†’ array of 5 slots
โ†’ each slot is int*
arr[0] = &a (address of a)
arr[1] = &b etc.
Function side
void fn(int **arr, int n)
โ†’ receives int** (ptr to first ptr)
arr[i] โ†’ the i-th pointer
*arr[i] โ†’ the actual integer
e5_array_of_pointers_to_fn.c
C
#include <stdio.h>

/* Function receives int** โ€” array of pointers passed in */
void printAll(int **arr, int n) {
    printf("  Values via *arr[i]:\n");
    for (int i = 0; i < n; i++) {
        printf("  arr[%d] points to address %p โ†’ value = %d\n",
               i, (void*)arr[i], *arr[i]);
    }
}

/* Function doubles every value through the pointers */
void doubleAll(int **arr, int n) {
    for (int i = 0; i < n; i++)
        *arr[i] *= 2;    /* dereference and modify */
}

/* Function finds maximum by comparing through pointers */
int findMax(int **arr, int n) {
    int max = *arr[0];
    for (int i = 1; i < n; i++)
        if (*arr[i] > max) max = *arr[i];
    return max;
}

int main() {
    /* Five separate int variables */
    int a = 10, b = 20, c = 30, d = 40, e = 50;

    /* Array of 5 pointers โ€” each holds an address */
    int *arr[5];
    arr[0] = &a;
    arr[1] = &b;
    arr[2] = &c;
    arr[3] = &d;
    arr[4] = &e;

    /* Pass array of pointers โ€” decays to int** */
    printf("=== printAll ===\n");
    printAll(arr, 5);

    printf("\n=== doubleAll ===\n");
    doubleAll(arr, 5);    /* modifies a,b,c,d,e through pointers */
    printAll(arr, 5);

    printf("\n=== findMax ===\n");
    printf("  Max = %d\n", findMax(arr, 5));

    /* Proof: a,b,c,d,e themselves changed */
    printf("\n=== original vars after doubleAll ===\n");
    printf("  a=%d b=%d c=%d d=%d e=%d\n", a, b, c, d, e);
    return 0;
}
output
=== printAll ===
  arr[0] points to address 0x7ffd... โ†’ value = 10
  arr[1] points to address 0x7ffd... โ†’ value = 20
  arr[2] points to address 0x7ffd... โ†’ value = 30
  arr[3] points to address 0x7ffd... โ†’ value = 40
  arr[4] points to address 0x7ffd... โ†’ value = 50

=== doubleAll ===
  arr[0] points to address 0x7ffd... โ†’ value = 20
  arr[1] points to address 0x7ffd... โ†’ value = 40
  arr[2] points to address 0x7ffd... โ†’ value = 60
  arr[3] points to address 0x7ffd... โ†’ value = 80
  arr[4] points to address 0x7ffd... โ†’ value = 100

=== findMax ===
  Max = 100

=== original vars after doubleAll ===
  a=20 b=40 c=60 d=80 e=100
๐Ÿ’ก
Why does the function receive int **arr? Because int *arr[5] is an array of pointers. When an array is passed to a function, it decays to a pointer to its first element. The first element is an int*. So a pointer to int* is int**. Two-step: int *arr[5] โ†’ first element type is int* โ†’ pointer to that is int**.
The variables a, b, c, d, e actually changed after doubleAll(). The function received pointers to them and wrote through those pointers with *arr[i] *= 2. This is how C functions modify multiple separate variables โ€” not by returning them, but by receiving their addresses and writing directly.
checklist
  • E1 โ€” Array IS a pointer: int *p = arr is same as int *p = &arr[0]. All four โ€” arr[i], *(arr+i), p[i], *(p+i) โ€” give the same result. p[2] is defined as *(p+2).
  • E2 โ€” Negative subscript: After p += 3, writing p[-1] is legal and means *(p-1) โ€” one step back. Legal as long as address stays inside the original array.
  • E3 โ€” Pointer to middle: int *right = arr + 5 creates a second view into the same array. right[0] IS arr[5] โ€” same memory, no copy. Pointer subtraction right - left gives the distance in elements.
  • E4 โ€” Double pointer: int **pp points to a pointer. *pp gives the pointer, **pp gives the value. Pass &p as int** to let a function change which variable p points at.
  • E5 โ€” Array of 5 pointers: int *arr[5] โ€” 5 slots, each an int*. Pass to function as int **arr (array decays to pointer-to-first-element, first element is int*, so pointer-to-that is int**). arr[i] = i-th pointer. *arr[i] = i-th value. Writing *arr[i] = x changes the original variable.