Post

Relation between array and pointers in c

Relation between array and pointers in c

Relation between array and pointers in c

Arrays, pointers, and function pointers are the brain-bending but powerful parts of C. I’ll give you a crisp list of important things, with small code examples and why they matter in real-world firmware/embedded dev.


1. Array ↔ Pointer relationship

  • In most expressions, an array name “decays” to a pointer to its first element.
1
2
3
int arr[] = {10, 20, 30};
int *p = arr;        // arr decays to &arr[0]
printf("%d\n", *(p+1)); // 20

But note: sizeof(arr) is different from sizeof(p).

1
2
printf("%zu\n", sizeof(arr)); // 12 (3 * 4 bytes)
printf("%zu\n", sizeof(p));   // 8 on 64-bit machine

Embedded trick: sizeof is your friend to know array length at compile-time.


2. Passing arrays to functions

1
2
3
4
5
6
7
void printArr(int *a, int n) {
    for(int i=0; i<n; i++) printf("%d ", a[i]);
}
int main() {
    int arr[5] = {1,2,3,4,5};
    printArr(arr, 5); // arr passed as pointer
}

Why? Because array “decays” to pointer. But compiler loses size info → you must pass length separately.


3. Pointer to whole array

Difference between: Are shown below

1
2
3
4
int arrc[5];
int *p = arrc;       // pointer to int
int ( *q )[ 5 ] = &arrc;     // pointer to entire array

  • p increments by one int.
  • q increments by 5 ints (entire block). Useful when dealing with multidimensional arrays.

4. 2D arrays vs pointers

1
2
3
int a[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
int (*p)[3] = a;  // pointer to array of 3 ints
printf("%d\n", p[1][2]); // 6

In firmware, this is handy when storing lookup tables (e.g., PWM duty cycles, ADC calibration tables).


5. Pointer arithmetic

1
2
3
int arr[] = { 10,20,30,40 };
int *p = arr;
printf("%d\n", *(p+2)); // 30

Embedded case: walking through a buffer (UART receive buffer, DMA buffer, etc).


6. Function pointers ( real firmware use)

Declare:

1
2
3
4
5
6
7
8
9
10
int add(int a, int b) { return a+b; }
int sub(int a, int b) { return a-b; }

int main() {
    int (*fp)(int,int); // function pointer
    fp = add;
    printf("%d\n", fp(3,4)); // 7
    fp = sub;
    printf("%d\n", fp(3,4)); // -1
}

Use cases in firmware:

  • Jump tables (replace huge switch).
  • ISR callbacks (like HAL_UART_RxCpltCallback in STM32 HAL).
  • State machine design.

7. Array of function pointers

Perfect for menu-driven systems or state machines:

1
2
3
4
5
6
7
8
9
void stateA() { printf("A\n"); }
void stateB() { printf("B\n"); }

void (*states[2])() = {stateA, stateB};

int main() {
    states[0](); // A
    states[1](); // B
}

This is gold in RTOS task tables, command handlers, or protocol parsers.


8. Pointer to function returning pointer

1
2
int* fun(int *x) { return x; }
int* (*fp)(int*) = fun;

Looks scary, but sometimes needed when working with library APIs or driver HAL layers.


9. Const correctness with arrays & pointers

1
2
const int *p;   // pointer to const data (can’t change *p)
int *const q;   // const pointer (can’t change q itself)

Used in firmware when you don’t want ISRs or tasks to corrupt lookup tables.


10. Volatile with arrays/pointers

1
2
3
#define TIMER_COUNT_REG (* ( (uint32_t volatile *) 0x40021000  ) )
volatile uint32_t *reg = (uint32_t*)0x40021000; 
*reg = 1;  // directly writing to peripheral register

Must-know in embedded: volatile stops compiler from optimizing out hardware register access.


This post is licensed under CC BY 4.0 by the author.