top of page

Q&A: C Code

  • Writer: Ashok Kumar Kumawat
    Ashok Kumar Kumawat
  • Apr 25
  • 6 min read

Q1: How do you find the size of an array in C?

Use sizeof(arr) / sizeof(arr[0]), which divides the total memory by the size of one element to get the number of elements.


#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int size = sizeof(arr) / sizeof(arr[0]);

    printf("Size of array: %d\n", size);
    return 0;
}
Explanation
  • sizeof(arr) → gives the total memory occupied by the array (in bytes).

  • sizeof(arr[0]) → gives the memory occupied by a single element.

  • Dividing them gives the number of elements in the array.

Output:

Code

Size of array: 5
Key Point

This method works only for arrays defined in the same scope. If you pass an array to a function, it decays into a pointer, and sizeof will no longer give the correct number of elements. In such cases, you should pass the size as a separate parameter.


The idea is: when you pass an array to a function, it “decays” into a pointer, so sizeof no longer gives the number of elements. You must pass the size separately.


Example: sizeof inside vs. outside a function

#include <stdio.h>

void printArray(int arr[]) {
    // Here arr is treated as a pointer, not a full array
    printf("Inside function: sizeof(arr) = %zu\n", sizeof(arr));
}

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int size = sizeof(arr) / sizeof(arr[0]);

    printf("In main: sizeof(arr) = %zu\n", sizeof(arr));
    printf("Number of elements = %d\n", size);

    printArray(arr);  // passing array to function

    return 0;
}
Explanation
  • In main, sizeof(arr) gives 20 bytes (5 integers × 4 bytes each).

  • Inside printArray, sizeof(arr) gives 8 bytes (on a 64-bit system) because arr is now just a pointer.

  • That’s why you must pass the size explicitly when calling a function.


Output (on a 64-bit system)
In main: sizeof(arr) = 20
Number of elements = 5
Inside function: sizeof(arr) = 8

Here's the fixed version that shows the correct way to handle array size when passing arrays to functions in C:


Correct Approach: Pass the size as a parameter

#include <stdio.h>

// Function now accepts both array and its size
void printArray(int arr[], int size) {
    printf("Inside function: received size = %d\n", size);
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int size = sizeof(arr) / sizeof(arr[0]);  // calculate size in main

    printf("In main: sizeof(arr) = %zu\n", sizeof(arr));
    printf("Number of elements = %d\n", size);

    // Pass both array and size
    printArray(arr, size);

    return 0;
}
Output (on a 64-bit system)
In main: sizeof(arr) = 20
Number of elements = 5
Inside function: received size = 5
10 20 30 40 50
Explanation
  • In main, sizeof(arr) correctly gives the total memory (20 bytes for 5 integers).

  • Inside printArray, the array has decayed into a pointer, so sizeof(arr) would only give the pointer size (typically 8 bytes).

  • By passing the size explicitly, the function can correctly iterate over all elements.


Q2: What is an array of pointers in C, and why is it useful?

An array of pointers is simply an array where each element stores the address of a variable (instead of storing the variable’s value directly). This is useful when you want to reference multiple values, strings, or dynamically allocated memory blocks without copying them.


Example 1: Array of Pointers to Integers
#include <stdio.h>

int main() {
    int a = 10, b = 20, c = 30;
    int *arr[3];   // array of 3 int pointers

    arr[0] = &a;
    arr[1] = &b;
    arr[2] = &c;

    for (int i = 0; i < 3; i++) {
        printf("Value at arr[%d] = %d\n", i, *arr[i]);
    }
    return 0;
}

Output:

Value at arr[0] = 10
Value at arr[1] = 20
Value at arr[2] = 30
Example 2: Array of Pointers to Strings

This is a very common use case in C.

#include <stdio.h>

int main() {
    const char *names[] = {"Ashok", "Kumar", "Kumawat"};

    for (int i = 0; i < 3; i++) {
        printf("Name[%d] = %s\n", i, names[i]);
    }
    return 0;
}

Output:

Name[0] = Ashok
Name[1] = Kumar
Name[2] = Kumawat
Explanation
  • int *arr[5]; → array of 5 pointers to int.

  • char *arr[3]; → array of 5 pointers to char (often used for strings).

  • Useful for:

    • Handling multiple strings efficiently.

    • Managing dynamic memory (malloc, calloc).

    • Avoiding duplication of large data by storing addresses instead of values.


Q3: How can function pointers be used to trigger callbacks in C?

A function pointer is a pointer that stores the address of a function. Just like you can have a pointer to a variable, you can also have a pointer to a function. This allows you to call functions dynamically, pass them as arguments, or register them as callbacks for events.

This is especially useful in embedded systems where hardware events (like a button press or sensor trigger) need to notify another module (like an LED driver). Instead of hardcoding which function to call, you register a function pointer as a callback.


Simple Example: Passing Function Pointer to triggerEvent
#include <stdio.h>

// Define a function pointer type
typedef void (*EventHandler)(void);

// A simple event trigger function that calls the passed function pointer
void triggerEvent(EventHandler handler) {
    printf("Event occurred!\n");
    handler();   // call the function via pointer
}

// Example callback function
void ledOn(void) {
    printf("LED turned ON!\n");
}

int main() {
    // Pass the function pointer to triggerEvent
    triggerEvent(ledOn);
    return 0;
}

Output:

Event occurred!
LED turned ON!
Explanation
  • typedef void (*EventHandler)(void); → defines a function pointer type.

  • triggerEvent(EventHandler handler) → accepts a function pointer and calls it when the event happens.

  • ledOn() → is the callback function that gets executed.

  • In main, we pass ledOn to triggerEvent.

This is the simplest form of callback using function pointers.


Embedded Industry Use Case

In embedded systems, this pattern is everywhere. For example:

  • Button press event: When a button is pressed, the hardware driver calls a registered callback (like turning on an LED or sending a notification).

  • UART receive event: When data arrives, the driver calls a callback function to process it.

  • Timer interrupt: When a timer expires, the callback function is invoked to perform an action.

LED Notification Example (embedded style):

c

#include <stdio.h>

// Define a function pointer type
typedef void (*EventCallback)(void);

// Global variable to hold registered callback
EventCallback ledCallback = NULL;

// Function to register LED notification callback
void registerLedNotification(EventCallback cb) {
    ledCallback = cb;
}

// Example LED handler function
void ledOnNotification(void) {
    printf("LED turned ON due to event!\n");
}

// Simulated event trigger
void triggerEvent(void) {
    printf("Event occurred!\n");
    if (ledCallback != NULL) {
        ledCallback();  // Call the registered callback
    }
}

int main() {
    // Register LED handler as callback
    registerLedNotification(ledOnNotification);

    // Simulate event
    triggerEvent();

    return 0;
}

This way, the LED module doesn’t need to know how the event is generated - it just reacts when the callback is invoked. That’s the power of function pointers in embedded C.


Q4: How can we determine the size of a struct in C without using sizeof or any library macros?

Normally, sizeof(struct abc) is the correct way to get the size of a struct. But if you want to avoid sizeof, you can use pointer arithmetic. The trick is to create an array of two structs and subtract their addresses. The difference gives you the size of one struct, including padding.


Example

#include <stdio.h>

struct abc {
    int a;
    float b;
};

int main() {
    struct abc arr[2];  // array of two structs

    // Difference between addresses of consecutive elements
    int size = (char*)&arr[1] - (char*)&arr[0];

    printf("Struct size (manual) = %d\n", size);
    printf("Struct size (sizeof) = %d\n", (int)sizeof(struct abc));

    return 0;
}
Output (on a typical 64-bit system)
Struct size (manual) = 8
Struct size (sizeof) = 8
Explanation
  • arr[0] and arr[1] are consecutive structs in memory.

  • Casting to (char*) ensures subtraction is in bytes.

  • The difference between their addresses equals the struct size (including padding).

  • This works because arrays store elements contiguously.


Key Point

This method is useful for learning and experimentation. In real-world code, always prefer sizeof because it is clearer, portable, and guaranteed to be correct across compilers.


Why size_t is better
  • Defined for sizes: size_t is the standard unsigned integer type returned by sizeof. It’s specifically meant to represent the size of objects in memory.

  • Portability: On different platforms, int may be 16, 32, or 64 bits. size_t automatically adjusts to the correct width for the architecture (e.g., 32-bit vs 64-bit).

  • Avoids negative values: Sizes and memory offsets can never be negative. size_t is unsigned, so it prevents accidental negative results.

  • Consistency: Functions in the C standard library (like malloc, strlen, memcpy) all use size_t for sizes and lengths. Using size_t keeps your code consistent with the standard.

// Correct way: use size_t
size_t size = (char*)&arr[1] - (char*)&arr[0];

printf("Struct size (manual) = %zu\n", size);
printf("Struct size (sizeof) = %zu\n", sizeof(struct abc));
Benefits of size_t
  • Prevents overflow/underflow issues when working with large arrays or memory blocks.

  • Matches the return type of sizeof, so no type mismatch warnings.

  • Makes your code more robust and portable across compilers and platforms.

Recent Posts

See All
Bubble Sort Concept

Bubble Sort repeatedly compares adjacent elements and swaps them if they are in the wrong order. After each pass, the largest element "bubbles up" to its correct position at the end. C Code Example #i

 
 
 
Storage Classes in Embedded C

Excellent, let’s now systematically cover Storage Classes in Embedded C with the structured breakdown you asked for. 1. Concept Overview Storage classes define scope, lifetime, and visibility of varia

 
 
 
Macros — Complete Guide for Embedded C

What is a Macro and How It Works at Compilation Stage Macros are handled by the C Preprocessor, which runs before the actual compilation begins. The compilation pipeline looks like this: Source.c →

 
 
 

Comments


© 2035 by Robert Caro. Powered and secured by Wix

bottom of page