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 -- (decrement) operator is a unary arithmetic operator that decreases the value of its operand by one. The operand must be a modifiable lvalue of a scalar type (an integer, a floating-point number, or a pointer to a complete object type). The operator exists in two distinct forms, which differ strictly in how the expression’s value computation is sequenced relative to the side effect of modifying the stored value of the operand.

Prefix Decrement

In the prefix form, the operator precedes the operand.
--operand;
Semantics:
  1. Side Effect: The value of the operand is decreased by 1.
  2. Value Computation: The expression evaluates to the new, decremented value of the operand.
The prefix decrement is semantically equivalent to operand -= 1. It is strictly not equivalent to operand = operand - 1 because the -- operator evaluates its operand exactly once. This prevents multiple evaluations of expressions with side effects (e.g., in --a[i++], the index i is incremented only once, whereas a[i++] = a[i++] - 1 evaluates i++ twice, invoking Undefined Behavior).

Postfix Decrement

In the postfix form, the operator succeeds the operand.
operand--;
Semantics:
  1. Value Computation: The expression evaluates to the original value of the operand (the value before the decrement occurs).
  2. Side Effect: The value of the operand is decreased by 1.
The value computation of the postfix expression is sequenced before the side effect of updating the stored value of the operand.

Value Category (Rvalue Result)

In C, the result of both the prefix and postfix decrement operators is an rvalue (a value, not an object). This is a critical distinction from C++ (where prefix decrement yields an lvalue). Because the result is an rvalue, it does not have an identifiable memory location, which strictly prohibits chaining decrement operators or taking the address of the result.
--(--x);   // Invalid: the inner --x yields an rvalue, but the outer -- requires an lvalue
&(--x);    // Invalid: cannot take the address of an rvalue

Type-Specific Mechanics

The exact mechanical behavior of the decrement operation depends on the scalar type of the lvalue:
  • Arithmetic Types (Integer/Floating-Point): The operator subtracts exactly 1 (or 1.0) from the current value.
  • Pointer Types: When applied to a pointer of type T*, T must be a complete object type. The operator does not strictly subtract 1 from the memory address; instead, it decrements the address by the size of the pointed-to type (sizeof(T)). Decrementing a pointer to an incomplete type (such as void* or a forward-declared struct) is a constraint violation in standard C because the size of the type is unknown.
int x = 5;
--x; // x becomes 4, expression evaluates to 4

double y = 5.5;
y--; // y becomes 4.5, expression evaluates to 5.5

int32_t *ptr = (int32_t*)0x1004;
ptr--; // ptr becomes 0x1000 (decremented by sizeof(int32_t), which is 4 bytes)

void *vptr = (void*)0x1004;
--vptr; // Invalid: void is an incomplete type

Technical Constraints and Undefined Behavior

  • Lvalue Requirement: The -- operator requires a modifiable lvalue. It cannot be applied to literals, constants, or the results of expressions that yield rvalues.
--5;          // Invalid: 5 is an rvalue
--(x + y);    // Invalid: (x + y) is an rvalue
  • Sequence Points: Modifying the same scalar object multiple times between two sequence points, or modifying an object and reading its value for a purpose other than determining the value to be stored, results in Undefined Behavior (UB).
x = x--;      // Undefined Behavior
y = --x - x;  // Undefined Behavior
Master C with Deep Grasping Methodology!Learn More