Expressions in C: Types, Examples, and How They're Evaluated
TL;DR: An expression in C is any combination of operands and operators that produces a value, such as a + b or x > 5. C has arithmetic, relational, logical, conditional, assignment, bitwise, and other expression types. Precedence and associativity decide how an expression is grouped, and type conversion decides the type of its result.

Almost every statement in a computer program written in C contains an expression. Adding two numbers, comparing values in an if statement, and incrementing a loop counter are all expressions, which is why knowing how to write them correctly is so important.

This guide explains what expressions in C are, walks through every major type with a working program, and shows how C evaluates them, including operator precedence, type conversion, and the common mistakes that trip up beginners. Every program below has been compiled with GCC under the C17 standard, with all warnings enabled.

If you're new to C, start with this C programming tutorial to understand the fundamentals before exploring expressions, operators, and evaluation rules. 

What Is an Expression in C?

An expression is a combination of operands (values, variables, or function calls) and operators (symbols such as +, >, or &&) that C evaluates to produce a single value.

Every expression has two properties:

  • A value: the result of evaluating it. 2 + 3 has the value 5.
  • A type: the kind of data that value is. 2 + 3 has type int, while 2.0 + 3 has type double.

Here are a few examples of expressions:

c = a + b
a - b + c
a + b - (a * c)

Even a single variable or constant, such as x or 42, is an expression, and an assignment like c = a + b is an expression too. Its value is the value assigned.

AI-Powered Full Stack Developer ProgramExplore Program
Want a Top Software Development Job? Start Here!

Operators, Operands, and Expressions

Expressions are built from operands and C operators, so understanding how each operator works makes it easier to predict the result of an expression.

Term

What it is

Example in total = price * 3

Operand

A value an operator works on

price, 3, total

Operator

A symbol that performs an operation

*, =

Expression

Operands and operators combined into something that produces a value

price * 3, and the whole total = price * 3

In short, operators and operands are the building blocks, and an expression is what you build with them. Understanding operators and expressions in C together is what lets you predict what a line of code will produce.

Types of Operators in C

Category

Operators

Example

Arithmetic

+ - * / %

a * b

Relational

== != > < >= <=

a > b

Logical

&& || !

a > 0 && b > 0

Bitwise

& | ^ ~ << >>

flags & 1

Assignment

= += -= *= /= %= &= |= ^= <<= >>=

x += 5

Increment and decrement

++ --

i++

Conditional (ternary)

? :

a > b ? a : b

Comma

,

i++, j--

Other

sizeof, (type) cast, & address, * dereference, . and -> member access

sizeof x

AI-Powered Full Stack Developer ProgramExplore Program
Here's How to Land a Top Software Developer Job

Types of Expressions in C

Expressions are usually named after the main operator they use. Here's each type with a complete program.

Arithmetic Expressions

An arithmetic expression uses arithmetic operators to calculate a numeric value. The result type depends on the operand types.

Example: z = a + b - (a * c), with a = 2, b = 3, and c = 4.

Precedence and associativity decide how C groups the expression:

  1. The parentheses make a * c one unit: 2 * 4 is 8.
  2. + and - have equal precedence and group left to right, so the expression becomes (a + b) - 8, which is 5 - 8.
  3. The result, -3, is assigned to z.
#include <stdio.h>

int main(void)
{
    int a = 2, b = 3, c = 4;
    int z = a + b - (a * c);

    printf("Result = %d\n", z);
    return 0;
}

Output:

Result = -3

Relational Expressions

A relational expression compares two values using >, <, >=, <=, ==, or !=. In C, the result is an int: 1 if the comparison is true and 0 if it's false.

Examples:

c = a > b
a * b == a + b
a + b * c > a * b + c

Take z = a * b > a + c with a = 3, b = 2, and c = 1. Arithmetic operators have higher precedence than relational ones, so C groups it as (a * b) > (a + c):

  1. a * b is 6.
  2. a + c is 4.
  3. 6 > 4 is true, so the expression's value is 1, and z becomes 1.
#include <stdio.h>

int main(void)
{
    int a = 3, b = 2, c = 1;
    int z = a * b > a + c;

    printf("Result = %d\n", z);
    return 0;
}

Output:

Result = 1

Logical Expressions

A logical expression combines conditions with && (AND), || (OR), or ! (NOT). Like relational expressions, it evaluates to 1 or 0. In C, any non-zero value counts as true.

Examples:

c = (a + b) > c && a < b
a > b || b > a

Take z = (a + b) > c && a < b with a = 2, b = 4, and c = 3:

  1. (a + b) > c is 6 > 3, which is true (1).
  2. a < b is 2 < 4, which is true (1).
  3. 1 && 1 is true, so z becomes 1.
#include <stdio.h>

int main(void)
{
    int a = 2, b = 4, c = 3;
    int z = (a + b) > c && a < b;

    printf("Result = %d\n", z);
    return 0;
}

Output:

Result = 1

&& and || also short-circuit: C stops evaluating as soon as the result is known. That behavior is covered in the evaluation section below.

Learn 45+ in-demand full-stack development skills and tools, including Frontend Development, Backend Development, Version Control and Collaboration, Database Management, and AI-Assisted Development, with our Full Stack Developer Course.

Conditional Expressions

A conditional expression uses the ternary operator? : to choose between two values:

condition ? value_if_true : value_if_false

If the condition is non-zero (true), the expression takes the second value. Otherwise, it takes the third.

Examples:

2 < 3 ? 2 : 3
a * b < c ? 1 : 0

Take z = (a * b < c) ? 1 : 0 with a = 5, b = 3, and c = 1:

  1. a * b is 15.
  2. 15 < 1 is false.
  3. Because the condition is false, the expression takes the value after the colon (0), and z becomes 0.
#include <stdio.h>

int main(void)
{
    int a = 5, b = 3, c = 1;
    int z = (a * b < c) ? 1 : 0;

    printf("Result = %d\n", z);
    return 0;
}

Output:

Result = 0

The condition in a ternary expression doesn't need a true or false keyword. In C, a comparison already produces 1 or 0. true and false are only available through <stdbool.h>, or as keywords starting with C23.

Assignment Expressions

An assignment expression stores a value in a variable. The value of the whole expression is the value that was assigned, which is why assignments can be chained. Compound operators such as += combine an operation with assignment.

#include <stdio.h>

int main(void)
{
    int x = 10;

    x += 5;   /* x = x + 5  */
    printf("After += 5: %d\n", x);
    x *= 2;   /* x = x * 2  */
    printf("After *= 2: %d\n", x);
    x -= 4;   /* x = x - 4  */
    printf("After -= 4: %d\n", x);
    x /= 3;   /* x = x / 3  */
    printf("After /= 3: %d\n", x);
    x %= 5;   /* x = x % 5  */
    printf("After %%= 5: %d\n", x);

    int p, q;
    p = q = 7;   /* assignment is right-associative: q = 7, then p = 7 */
    printf("p = %d, q = %d\n", p, q);
    return 0;
}

Output:

After += 5: 15
After *= 2: 30
After -= 4: 26
After /= 3: 8
After %= 5: 3
p = 7, q = 7

Notice that 26 / 3 gives 8, not 8.67. Dividing two integers in C discards the fractional part, which is covered in more detail below.

Expand your programming expertise with Simplilearn's Java Certification Training. Build a strong foundation in Core Java, Java EE, Spring, Hibernate, and multithreading while gaining practical experience through 35+ coding exercises and hands-on web projects.

Increment and Decrement Expressions

++ adds 1 to a variable and -- subtracts 1. Their position matters:

  • Prefix (++i): changes the variable first, then uses the new value.
  • Postfix (i++): uses the current value first, then changes the variable.
#include <stdio.h>

int main(void)
{
    int i = 5;

    int a = i++;   /* a gets 5, then i becomes 6 */
    int b = ++i;   /* i becomes 7, then b gets 7 */

    printf("a = %d, b = %d, i = %d\n", a, b, i);
    return 0;
}

Output:

a = 5, b = 7, i = 7

Bitwise Expressions

A bitwise expression works on the individual bits of integer values. It's common in low-level code for flags, masks, and hardware registers.

#include <stdio.h>

int main(void)
{
    int a = 12;   /* binary 1100 */
    int b = 10;   /* binary 1010 */

    printf("a & b  = %d\n", a & b);    /* 1000 */
    printf("a | b  = %d\n", a | b);    /* 1110 */
    printf("a ^ b  = %d\n", a ^ b);    /* 0110 */
    printf("~a     = %d\n", ~a);
    printf("a << 1 = %d\n", a << 1);   /* 11000 */
    printf("a >> 2 = %d\n", a >> 2);   /* 0011 */
    return 0;
}

Output:

a & b  = 8
a | b  = 14
a ^ b  = 6
~a     = -13
a << 1 = 24
a >> 2 = 3

~a is -13 because ~ flips every bit of the int, including the sign bit, and on standard two's complement systems ~x equals -x - 1. Shifting left by 1 doubles a non-negative value, and shifting right by 2 divides it by 4.

Comma Expressions

The comma operator evaluates its left operand, discards the result, then evaluates its right operand. The value of the whole expression is the right operand. It's most often used to update two variables in a for loop.

#include <stdio.h>

int main(void)
{
    int a;
    int x = (a = 3, a * 2);   /* a = 3 runs first, then a * 2 is the value */

    printf("a = %d, x = %d\n", a, x);

    for (int i = 0, j = 10; i < j; i += 3, j -= 3)
        printf("i = %d, j = %d\n", i, j);
    return 0;
}

Output:

a = 3, x = 6
i = 0, j = 10
i = 3, j = 7

Integer, Floating-Point, Mixed-Mode, and Constant Expressions

Expressions can also be grouped by the data type of their operands and result:

  • Integer expressions use only integer operands, and produce an integer. 7 / 2 is 3, because integer division discards the remainder.
  • Floating-point expressions use float or double operands, and keep the fractional part. 7.0 / 2.0 is 3.5.
  • Mixed-mode expressions combine integer and floating-point operands. C converts the integer to floating-point first, so the result is floating-point.
  • Constant expressions contain only constants, so the compiler can evaluate them before the program runs. They're required in places such as array sizes and case labels.
#include <stdio.h>

#define SIZE 5

int main(void)
{
    /* Integer expressions */
    printf("7 / 2  = %d\n", 7 / 2);
    printf("7 %% 2  = %d\n", 7 % 2);

    /* Floating-point expression */
    printf("7.0 / 2.0 = %.2f\n", 7.0 / 2.0);

    /* Mixed-mode expressions */
    int items = 7;
    double price = 2.5;
    printf("items * price = %.2f\n", items * price);
    printf("items / 2     = %d\n", items / 2);
    printf("items / 2.0   = %.2f\n", items / 2.0);
    printf("(double) items / 2   = %.2f\n", (double) items / 2);
    printf("(double) (items / 2) = %.2f\n", (double) (items / 2));

    /* Constant expression: evaluated at compile time */
    int scores[SIZE * 2];
    printf("Array holds %zu ints\n", sizeof scores / sizeof scores[0]);
    return 0;
}

Output:

7 / 2  = 3
7 % 2  = 1
7.0 / 2.0 = 3.50
items * price = 17.50
items / 2     = 3
items / 2.0   = 3.50
(double) items / 2   = 3.50
(double) (items / 2) = 3.00
Array holds 10 ints

The last two lines show why the position of a cast matters. (double) items / 2 converts items before dividing, so the result is 3.5. (double) (items / 2) divides two integers first, which gives 3, and only then converts 3 to 3.0.

Java Certification TrainingENROLL NOW
Dive Deep Into Java Core Concepts

How Expressions Are Evaluated in C

Three sets of rules decide the result of an expression: precedence and associativity, the order in which operands are evaluated, and type conversion.

Operator Precedence and Associativity

Precedence decides which operators group first when an expression has several. Associativity decides the grouping when operators share the same precedence.

Precedence

Operators

Associativity

1 (highest)

() [] -> . postfix ++ --

Left to right

2

Unary + - ! ~ prefix ++ -- (type) * & sizeof

Right to left

3

* / %

Left to right

4

+ -

Left to right

5

<< >>

Left to right

6

< <= > >=

Left to right

7

== !=

Left to right

8

&

Left to right

9

^

Left to right

10

|

Left to right

11

&&

Left to right

12

||

Left to right

13

? :

Right to left

14

= += -= *= /= %= &= ^= |= <<= >>=

Right to left

15 (lowest)

,

Left to right

Here's the table in action:

#include <stdio.h>

int main(void)
{
    printf("2 + 3 * 4   = %d\n", 2 + 3 * 4);     /* * before +          */
    printf("(2 + 3) * 4 = %d\n", (2 + 3) * 4);   /* parentheses first   */
    printf("20 / 4 / 2  = %d\n", 20 / 4 / 2);    /* left to right       */
    printf("10 - 4 - 3  = %d\n", 10 - 4 - 3);    /* left to right       */
    printf("-3 * 2      = %d\n", -3 * 2);        /* unary minus binds tightest */
    printf("5 + 3 > 7 && 2 < 1 = %d\n", 5 + 3 > 7 && 2 < 1);   /* +, then >, <, then && */
    return 0;
}

Output:

2 + 3 * 4   = 14
(2 + 3) * 4 = 20
20 / 4 / 2  = 2
10 - 4 - 3  = 3
-3 * 2      = -6
5 + 3 > 7 && 2 < 1 = 0

20 / 4 / 2 is 2 because division groups left to right: (20 / 4) / 2. If it grouped right to left, it would be 20 / (4 / 2), which is 10.

When in doubt, add parentheses. They cost nothing and make your intent clear to anyone reading the code.

Grouping Is Not the Same as Evaluation Order

Precedence tells you how an expression is grouped, not which operand the computer calculates first. In a * b + c * d, precedence guarantees that the two products are added, but C doesn't say whether a * b or c * d is computed first. For most expressions, this doesn't change the result.

It does matter when an expression changes a variable and also reads it. An expression like i = i++ + 1 modifies i twice without a defined order, which is undefined behavior. The program might print anything, and different compilers can give different results. GCC flags this with the warning "operation on 'i' may be undefined." The fix is simple: don't modify a variable more than once in the same expression.

The exceptions are &&, ||, ? :, and the comma operator, which always evaluate their left operand first.

Short-Circuit Evaluation

&& and || stop as soon as the result is known. With &&, if the left side is false, the right side is never evaluated. With ||, if the left side is true, the right side is skipped. This makes it safe to guard risky operations:

#include <stdio.h>

int main(void)
{
    int x = 0;

    /* x != 0 is false, so 10 / x is never evaluated: no division by zero */
    if (x != 0 && 10 / x > 1)
        printf("Large enough\n");
    else
        printf("Skipped the division safely\n");

    int count = 0;
    int result = (1 || ++count);   /* ++count never runs */
    printf("result = %d, count = %d\n", result, count);
    return 0;
}

Output:

Skipped the division safely
result = 1, count = 0

Type Conversion in Expressions

When an expression mixes types, C converts operands to a common type before calculating. The main rules are:

  • Integer promotion: char and short values are converted to int before arithmetic.
  • Usual arithmetic conversions: if one operand is double and the other is int, the int is converted to double. In general, the "smaller" type is converted to the "larger" one.
  • Assignment conversion: the result is converted to the type of the variable it's stored in. Assigning 3.9 to an int stores 3.

This is why items / 2.0 gives 3.5 in the earlier program: items is converted to double because 2.0 is a double.

Java Certification TrainingENROLL NOW
Master Core Java 8 Concepts, Java Servlet, & More!

Common Mistakes With Expressions in C

Each of these compiles, which is what makes them dangerous. GCC warns about all of them when you compile with -Wall -Wextra, so keep warnings turned on.

1. Chaining comparisons. 1 < x < 3 does not check whether x is between 1 and 3. It evaluates 1 < x first, which gives 1 or 0, then compares that result with 3, so it's always true.

int x = 5;
printf("1 < x < 3        -> %d\n", 1 < x < 3);          /* (1 < 5) is 1, then 1 < 3 */
printf("1 < x && x < 3   -> %d\n", 1 < x && x < 3);

1 < x < 3        -> 1
1 < x && x < 3   -> 0

2. Forgetting that == binds tighter than &. flags & 1 == 0 is read as flags & (1 == 0), which is always 0.

int flags = 2;
printf("flags & 1 == 0   -> %d\n", flags & 1 == 0);      /* flags & (1 == 0) */
printf("(flags & 1) == 0 -> %d\n", (flags & 1) == 0);

flags & 1 == 0   -> 0
(flags & 1) == 0 -> 1

3. Using = instead of == in a condition. if (y = 0) assigns 0 to y, and the condition is then always false.

4. Dividing integers before converting. double avg = 7 / 2; stores 3.00, not 3.50, because 7 / 2 is integer division. Write 7 / 2.0 or cast one operand first.

5. Modifying a variable twice in one expression. As covered above, i = i++ + 1 is undefined behavior. Split it into separate statements.

Wondering how Software Engineers reach senior and leadership roles? Explore the skills, technologies, salary growth, and career progression behind one of the world's most in-demand jobs with this software engineer roadmap.

Key Takeaways

  • An expression in C combines operands and operators to produce a value, and every expression has both a value and a type.
  • Expression types include arithmetic, relational, logical, conditional, assignment, increment and decrement, bitwise, and comma, plus integer, floating-point, mixed-mode, and constant expressions.
  • Relational and logical expressions produce 1 for true and 0 for false.
  • Precedence and associativity decide how an expression is grouped, but not the order in which operands are calculated.
  • Integer division, type conversion, and short-circuiting explain most surprising results, so use parentheses and compiler warnings to avoid mistakes.

Conclusion

Expressions are the core of every C program, from simple arithmetic to the conditions that control loops. Once you know how C groups operators, converts types, and short-circuits logical tests, you can predict exactly what each line will produce and avoid the subtle bugs that catch many beginners.

If you're ready to build practical development skills, explore Simplilearn's AI-Powered Full Stack Developer Course, which covers front-end and back-end development. You can also explore Simplilearn's software development courses to learn new technologies, strengthen your programming skills, and prepare for software development roles.

FAQs

1. What is the difference between an expression and a statement in C?

An expression produces a value, such as a + b. A statement is a complete instruction that the program executes. Adding a semicolon to an expression turns it into an expression statement, as in a = a + b;. Statements such as if, for, and return often contain expressions, but aren't expressions themselves.

2. What are lvalues and rvalues in C?

An lvalue refers to a memory location that can appear on the left side of an assignment, such as a variable name or arr[2]. An rvalue is just a value, such as 5 or a + b, which can't be assigned to. That's why x = 5 is valid, but 5 = x and a + b = 10 are compile errors.

3. What is a side effect in a C expression?

A side effect is any change an expression makes besides producing its value, such as assigning a variable, incrementing it with ++, or printing output through a function call. Side effects are useful, but combining several on the same variable in one expression can cause undefined behavior.

4. Does C evaluate function arguments from left to right?

No. The order in which C evaluates function arguments is unspecified, so that it can differ between compilers. A call like printf("%d %d", i++, i++) is also undefined behavior, because it modifies i twice without a defined order. Calculate values in separate statements before passing them.

5. How do division and modulus work with negative numbers in C?

Since C99, integer division truncates toward zero. So -7 / 2 is -3 and 7 / -2 is also -3. The result of % takes the sign of the left operand: -7 % 2 is -1, while 7 % -2 is 1.

About the Author

Aditya KumarAditya Kumar

Aditya Kumar is an experienced analytics professional with a strong background in designing analytical solutions. He excels at simplifying complex problems through data discovery, experimentation, storyboarding, and delivering actionable insights.

View More
  • Acknowledgement
  • PMP, PMI, PMBOK, CAPM, PgMP, PfMP, ACP, PBA, RMP, SP, OPM3 and the PMI ATP seal are the registered marks of the Project Management Institute, Inc.
  • *All trademarks are the property of their respective owners and their inclusion does not imply endorsement or affiliation.
  • Career Impact Results vary based on experience and numerous factors.