Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The * token in C is a context-dependent operator and syntactic marker that serves four distinct roles: as a unary indirection (dereference) operator, as a pointer declarator in type definitions, as a binary arithmetic multiplication operator, and as an unspecified size indicator for variable-length arrays (VLAs) in function prototypes.

1. Unary Indirection (Dereference) Operator

When used as a unary operator preceding an expression, * performs indirection. It evaluates to the object or function to which the operand points. Mechanics:
  • Operand Requirement: The operand must have a pointer type.
  • Return Value: The result is an lvalue (a locator value) designating the object or function located at the memory address stored in the pointer. If the operand points to an incomplete type, the resulting lvalue cannot be accessed (read or written) until the type is completed, but forming the lvalue itself is strictly valid (e.g., to be used as the operand of the unary & operator).
  • Type Yielded: If the operand is of type “pointer to T”, the result of the indirection is of type “T”.
  • Undefined Behavior (UB): Evaluating the * operator invokes undefined behavior if the operand is a null pointer, points outside the bounds of an allocated object, or contains an address that is inappropriately aligned for the pointed-to type.
  • Uninitialized Memory: Forming an lvalue via * to write to uninitialized memory (e.g., immediately after allocation) is valid. However, using the * operator to read from uninitialized memory invokes undefined behavior if it results in a trap representation.
void indirection_example(void) {
    int val = 42;
    int *ptr = &val;

    /* Evaluates to the lvalue of 'val', allowing read/write access */
    *ptr = 100; 

    struct Node;
    struct Node *next_node = NULL; /* Initialized to prevent UB on read */
    
    /* Valid: forms an lvalue of an incomplete type, then takes its address.
       &* cancels out, evaluating to the value of next_node. */
    struct Node *same_node = &*next_node; 
}

2. Pointer Declarator

In declarations, * acts as a type modifier rather than an executable operator. It modifies the base type to indicate that the declared entity is a pointer. Mechanics:
  • Binding: The * binds to the direct declarator to its right. Because postfix operators like [] and () have higher precedence than * in declaration syntax, the * often applies to a larger declarator construct rather than just the identifier. For example, in int *arr[5];, the identifier arr binds to [5] first, and the * applies to the array arr[5], resulting in an array of pointers, not a pointer to an array.
  • Chaining: Multiple * tokens can be chained to create multiple levels of indirection (e.g., pointer to a pointer).
  • Qualifiers: Type qualifiers (const, volatile, restrict) can be placed immediately after the * to qualify the pointer itself, rather than the pointed-to type.
/* 'a' is a pointer to int. 'b' is a standard int. */
int *a, b; 

/* 'arr' is an array of 5 pointers to int */
int *arr[5];

/* 'ptr_to_ptr' is a pointer to a pointer to a char */
char **ptr_to_ptr; 

/* 'c' is a constant pointer to a mutable int */
int * const c; 

3. Binary Multiplication Operator

When placed between two expressions, * functions as the binary arithmetic multiplication operator. Mechanics:
  • Operand Requirement: Both operands must have arithmetic types (integer or floating-point).
  • Type Conversion: The compiler applies usual arithmetic conversions to the operands to establish a common real type before performing the operation.
  • Overflow Behavior:
    • Signed Integers: If the mathematical result cannot be represented in the resulting type, the operation invokes undefined behavior (signed overflow).
    • Unsigned Integers: The result is reduced modulo 2N2^N (where NN is the number of bits in the resulting type), meaning it wraps around predictably.
    • Floating-Point: If the mathematical result exceeds the representable range, the behavior depends on the implementation. If the implementation strictly adheres to IEEE 754 (IEC 60559), overflow evaluates to ±\pm\infty; otherwise, floating-point overflow invokes undefined behavior.
void multiplication_example(void) {
    int x = 5;
    int y = 10;

    /* Binary multiplication yielding an rvalue */
    int z = x * y; 
}

4. Unspecified Size Indicator (C99)

Introduced in C99, the * token can be used inside array declarators exclusively within function prototype declarations (not function definitions). It indicates that the parameter is a Variable Length Array (VLA) of an unspecified size. Mechanics:
  • Scope: It is only syntactically valid in the array size expression of a function prototype scope.
  • Purpose: It allows the compiler to properly parse multi-dimensional VLA parameters without requiring the size expressions to be evaluated or specified at the point of declaration.
/* '*' indicates 'matrix' is a VLA with an unspecified column size in the prototype */
void process_matrix(int rows, int cols, int matrix[rows][*]);

/* In the actual function definition, the size must be specified */
void process_matrix(int rows, int cols, int matrix[rows][cols]) {
    /* Implementation */
}

Precedence and Associativity

The parsing of the * operator is strictly governed by C’s precedence rules:
RolePrecedence LevelAssociativity
Unary IndirectionHigh (Level 2, alongside ++, --, &)Right-to-Left
Binary MultiplicationMedium (Level 3, alongside /, %)Left-to-Right
Because unary * has higher precedence than binary arithmetic operators, an expression like *ptr * 5 is unambiguously parsed as (*ptr) * 5. However, postfix operators (like array subscripting [] or struct member access ->, .) have higher precedence than unary *. Therefore, *arr[0] is parsed as *(arr[0]), not (*arr)[0].
Master C with Deep Grasping Methodology!Learn More