Core C Programming for Embedded Development

Core Components of a C Program

A typical C application comprises preprocessor directives, functions, variables, expressions, and comments.

Data Types

The memory footprint of data types varies depending on the architecture (e.g., 32-bit vs 64-bit systems).

Integer Types

TypeSizeValue Range
char1 byte-128 to 127 or 0 to 255
unsigned char1 byte0 to 255
signed char1 byte-128 to 127
int2 or 4 bytes-32,768 to 32,767 or -2,147,483,648 to 2,147,483,647
unsigned int2 or 4 bytes0 to 65,535 or 0 to 4,294,967,295
short2 bytes-32,768 to 32,767
unsigned short2 bytes0 to 65,535
long4 bytes-2,147,483,648 to 2,147,483,647
unsigned long4 bytes0 to 4,294,967,295

Floating-Point Types

TypeSizeValue RangePrecision
float4 bytes1.2E-38 to 3.4E+386 decimal places
double8 bytes2.3E-308 to 1.7E+30815 decimal places
long double16 bytes3.4E-4932 to 1.1E+493219 decimal places

Void Type

UsageDescription
Function returns voidIndicates a function does not return a value. Example: void exit_app(int status);
Function parameters voidSignifies a function takes no arguments. Example: int get_rand(void);
Void pointersRepresents a pointer to an unknown type. Memory functions like void *malloc(size_t bytes); return generic pointers that can be cast to any type.

Lvalues and Rvalues

Expressions in C are categorized into two types:

  • Lvalue: An expression referring to a memory address. Lvalues can appear on either side of an assignment operator.
  • Rvalue: An expression representing a data value stored at some address. Rvalues cannot be assigned to and must appear only on the right side of an assignment.

Variables act as lvalues, while numeric literals act as rvalues. Valid statement:

int age = 42;

Invalid statement (compilation error):

10 = 42;

Preprocessor Macros vs Constants

Both #define and const create constant values, but const is generally preferred for type safety and scoping.

  • Mechanism: #define performs textual substitution before compilation. const declares a typed variable whose value is fixed at runtime or compile-time.
  • Type Safety: #define lacks type checking. const variables have explicit types, enabling the compiler to catch mismatched operations.
  • Scope: Macros defined with #define persist globally from their point of definition. const variables respect block-level scoping rules.
  • Debugging: Macros are invisible in the debugger's symbol table. const variables retain their names in the symbol table, easing debugging.

Storage Classes

Storage classes dictate the visibility, memory location, and lifespan of variables.

  • auto
  • register
  • static
  • extern

auto

The default storage class for local variables. They are allocated upon entering a block and deallocated upon exiting.

{
   int idx;
   auto int count;
}

register

Advises the compiler to store the variable in a processor register rather than RAM for rapid access. You cannot use the address-of operator (&) on register variables.

{
   register int fast_access_var;
}

The compiler may ignore this hint if hardware constraints prevent it.

static

Tells the compiler to maintain the variable's existence throughout the program's execution rather than recreating it on each scope entry. For global variables, static restricts visibility to the defining translation unit.

#include <stdio.h>

void increment_counter(void);
static int loop_limit = 3;

int main() {
    while (loop_limit--) {
        increment_counter();
    }
    return 0;
}

void increment_counter(void) {
    static int persistent_val = 10;
    persistent_val += 5;
    printf("Persistent: %d, Limit: %d\n", persistent_val, loop_limit);
}

Output:

Persistent: 15, Limit: 2
Persistent: 20, Limit: 1
Persistent: 25, Limit: 0

extern

Used to declare a variable or function defined in another source file. It does not allocate new memory but references an existing entity.

File: main_app.c

#include <stdio.h>
int shared_data;
extern void print_shared();

int main() {
    shared_data = 100;
    print_shared();
    return 0;
}

File: helper_func.c

#include <stdio.h>
extern int shared_data;

void print_shared(void) {
    printf("Shared value: %d\n", shared_data);
}

Compile with gcc main_app.c helper_func.c. Running the program outputs: Shared value: 100

Operators

Arithmetic Operators

Increment operators have distinct behaviors based on prefix/suffix placement:

  • var++: Returns current value, then increments.
  • ++var: Increments value, then returns new value.

Logical Operators

OperatorDescriptionExample
&&Logical AND. True if both operands are non-zero.(X && Y) is false
||Logical OR. True if any operand is non-zero.(X || Y) is true
!Logical NOT. Reverses the logical state of the operand.!(X && Y) is true

Bitwise Operators

Operate on individual bits. Let X = 12 (0000 1100) and Y = 10 (0000 1010):

OperatorDescriptionResult
&Bitwise AND: Sets bit to 1 if both bits are 1.X & Y = 8 (0000 1000)
|Bitwise OR: Sets bit to 1 if either bit is 1.X | Y = 14 (0000 1110)
^Bitwise XOR: Sets bit to 1 if bits are different.X ^ Y = 6 (0000 0110)
~Bitwise NOT: Inverts all bits.~X = -13 (1111 0011)
<<Left Shift: Moves bits left, filling with 0s. Equivalent to multiplying by 2^n.X << 2 = 48 (0011 0000)
>>Right Shift: Moves bits right. Equivalent to dividing by 2^n.X >> 2 = 3 (0000 0011)

Assignment Operators

OperatorDescriptionEquivalent
=Simple assignmentZ = X + Y
+=Add and assignZ += X is Z = Z + X
-=Subtract and assignZ -= X is Z = Z - X
*=Multiply and assignZ *= X is Z = Z * X
/=Divide and assignZ /= X is Z = Z / X
%=Modulo and assignZ %= X is Z = Z % X
<<=Left shift and assignZ <<= 2 is Z = Z << 2
>>=Right shift and assignZ >>= 2 is Z = Z >> 2
&=Bitwise AND and assignZ &= 2 is Z = Z & 2
^=Bitwise XOR and assignZ ^= 2 is Z = Z ^ 2
|=Bitwise OR and assignZ |= 2 is Z = Z | 2

Miscellaneous Operators

OperatorDescriptionExample
sizeof()Yields the memory size of a variable or type.sizeof(int) returns 4
&Yields the memory address of a variable.&val
*Dereferences a pointer to access the pointed value.*ptr
? :Ternary conditional operator.cond ? val_if_true : val_if_false

Operator Precedence

Operators with higher precedence bind tighter. For instance, x = 5 + 3 * 2 evaluates to 11 because * outranks +.

CategoryOperatorsAssociativity
Postfix() [] -> . ++ --Left to right
Unary+ - ! ~ ++ -- (type) * & sizeofRight to left
Multiplicative* / %Left to right
Additive+ -Left to right
Shift<< >>Left to right
Relational< <= > >=Left to right
Equality== !=Left to right
Bitwise AND&Left to right
Bitwise XOR^Left to right
Bitwise OR|Left to right
Logical AND&&Left to right
Logical OR||Left to right
Conditional?:Right to left
Assignment= += -= *= /= %= >>= <<= &= ^= |=Right to left
Comma,Left to right

Functions

Function Parameters

Formal parameters receive data when a function is invoked. They behave like local variables. C supports two calling conventions:

MethodDescription
Call by ValueCopies the argument's value into the parameter. Altering the parameter inside the function does not affect the original argument.
Call by ReferencePasses the argument's address via pointers. Dereferencing the pointer allows the function to modify the original variable.

Arrays

ConceptDetails
Multi-dimensionalSupports arrays of arrays, such as 2D matrices.
Passing to functionsArray names decay to pointers, allowing them to be passed as pointer arguments.
Returning from functionsFunctions can return pointers to dynamically allocated arrays.
Pointer to an arrayAn array name acts as a pointer to its first element.
Static vs DynamicStatic arrays have fixed sizes determined at compile time; dynamic arrays use memory allocation (e.g., malloc) to resize at runtime.

Pointers

ConceptDetails
ArithmeticPointers can be incremented (++), decremented (--), or have integers added/subtracted.
Array of pointersArrays designed to hold memory addresses.
Pointer to pointerA construct where a pointer stores the address of another pointer.
Passing to functionsEnables functions to alter external variables via reference passing.
Returning from functionsFunctions can yield pointers to static or dynamically allocated memory.

String Manipulation Functions

FunctionPurpose
strcpy(dest, src)Copies the source string into the destination buffer.
strcat(dest, src)Appends the source string to the end of the destination string.
strlen(str)Calculates the length of the string, excluding the null terminator.
strcmp(str1, str2)Returns 0 if identical, a negative value if str1 < str2, and a positive value if str1 > str2.
strchr(str, ch)Locates the first occurrence of a character in a string.
strstr(str1, str2)Finds the first occurrence of the substring str2 within str1.

Tags: c programming Embedded Systems Data Types Storage Classes Operators

Posted on Tue, 06 Oct 2026 16:40:08 +0000 by Iklekid