Chapter 2: Core Concepts: Data Types, Variables, and Operators

Chapter 2: Core Concepts: Data Types, Variables, and Operators

Now that your development environment is set up, it’s time to dive into the fundamental building blocks of C programming. In this chapter, we will explore:

  • Data Types: How C classifies different kinds of information (numbers, characters, etc.).
  • Variables: How to store and name data in your programs.
  • Operators: Symbols that perform operations on data (like addition, assignment, comparison).

These concepts are the ABCs of any programming language, and mastering them in C will provide a solid foundation for more complex topics.

2.1 Data Types

In C, every variable must have a data type. This type tells the compiler two crucial things:

  1. How much memory to allocate for the variable.
  2. How to interpret the bits stored in that memory location.

C provides a set of basic (primitive) data types:

2.1.1 Integer Types

These are used to store whole numbers (numbers without a fractional part).

TypeDescriptionTypical Size (Bytes)Range (example)
charUsed for single characters. Can also be used for small integer values. signed char and unsigned char explicitly specify signedness.1signed: -128 to 127, unsigned: 0 to 255
shortShort integer. signed short and unsigned short. Guarantees at least 16 bits.2signed: -32,768 to 32,767, unsigned: 0 to 65,535
intThe most common integer type. signed int and unsigned int. Guarantees at least 16 bits, but typically 32 bits on modern systems.2 or 4signed: -2,147,483,648 to 2,147,483,647 (for 32-bit), unsigned: 0 to 4,294,967,295
longLong integer. signed long and unsigned long. Guarantees at least 32 bits.4 or 8signed: -2,147,483,648 to 2,147,483,647 (for 32-bit)
long longVery long integer (introduced in C99). signed long long and unsigned long long. Guarantees at least 64 bits.8signed: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
_BitInt(N) (C23)Bit-precise integer type for N bits, where N is an integer literal from 1 to LLONG_MAX. Can be signed or unsigned. Useful for memory-constrained devices.ceil(N/8)Depends on N

Important Notes:

  • The exact size and range of short, int, and long can vary between systems and compilers, but they adhere to minimum size guarantees. char is always 1 byte.
  • Prefixing with unsigned means the variable can only store non-negative values, doubling its positive range. By default, integer types are signed.
  • Suffixes are used to specify the type of integer literals:
    • L or l for long (e.g., 123456789L)
    • LL or ll for long long (e.g., 9876543210LL)
    • U or u for unsigned (e.g., 100U)
    • Combinations: 100UL (unsigned long), 200ULL (unsigned long long)
  • C23 Binary Literals and Digit Separators:
    • You can now write binary numbers with 0b or 0B prefix: 0b10101010.
    • You can use a single quote ' as a digit separator for readability: 1'000'000, 0x'FF'FF, 0b'1111'0000.

2.1.2 Floating-Point Types

These are used to store numbers with a fractional part (real numbers).

TypeDescriptionTypical Size (Bytes)Precision (Decimal Digits)
floatSingle-precision floating-point number.4~6-7
doubleDouble-precision floating-point number (most common).8~15-17
long doubleExtended-precision floating-point number.10 or 16~18-19 (depends on system)

Important Notes:

  • double is generally preferred for floating-point calculations due to its higher precision.
  • Floating-point literals are double by default (e.g., 3.14). Use f or F suffix for float (e.g., 3.14f) and l or L for long double (e.g., 3.14L).

2.1.3 Character Type

  • char: Stores a single character (like ‘A’, ‘b’, ‘7’, ‘$’). Internally, characters are stored as integer values (ASCII or Unicode representation). A char is an integer type 1 byte wide.

2.1.4 _Bool (C99) / bool (C23)

  • _Bool (C99): Used to store boolean values (true or false). It behaves like an integer type, where 0 is false and any non-zero value is true.
  • bool (C23): C23 introduces bool, true, and false as keywords, meaning you no longer need to include <stdbool.h> to use them.

Code Example: Data Type Sizes and Values

Let’s see the sizes of these data types on your system and declare some variables.

#include <stdio.h>    // For printf
#include <limits.h>   // For integer limits (e.g., INT_MAX, CHAR_MIN)
#include <float.h>    // For float limits (e.g., FLT_MAX, DBL_MAX)
#include <stdbool.h>  // For C99 bool, true, false (optional in C23)

int main() {
    printf("--- Size of Data Types (in bytes) ---\n");
    printf("Size of char: %zu byte(s)\n", sizeof(char));
    printf("Size of short: %zu byte(s)\n", sizeof(short));
    printf("Size of int: %zu byte(s)\n", sizeof(int));
    printf("Size of long: %zu byte(s)\n", sizeof(long));
    printf("Size of long long: %zu byte(s)\n", sizeof(long long));
    printf("Size of float: %zu byte(s)\n", sizeof(float));
    printf("Size of double: %zu byte(s)\n", sizeof(double));
    printf("Size of long double: %zu byte(s)\n", sizeof(long double));
    printf("Size of _Bool: %zu byte(s)\n", sizeof(_Bool)); // Or sizeof(bool) in C23

    printf("\n--- Integer Values and Ranges ---\n");
    int age = 30;
    unsigned int population = 8000000000U; // U suffix for unsigned
    long int distance_to_sun = 149600000000L; // L suffix for long
    long long big_number = 123456789012345LL; // LL suffix for long long
    signed char ascii_val = 'A'; // 'A' is 65 in ASCII
    _BitInt(10) small_int = 512; // C23 feature: 10-bit integer

    printf("Age: %d\n", age);
    printf("World Population: %u\n", population); // %u for unsigned int
    printf("Distance to Sun: %ld km\n", distance_to_sun); // %ld for long int
    printf("A very big number: %lld\n", big_number); // %lld for long long
    printf("ASCII value 'A': %d\n", ascii_val);

    // Using C23 binary literal and digit separator
    int binary_value = 0b1010'1010;
    printf("Binary 0b1010'1010: %d\n", binary_value);

    // _BitInt(N) example (requires C23 compiler)
    printf("10-bit integer value: %d\n", small_int);


    printf("\n--- Floating Point Values ---\n");
    float pi_float = 3.1415926535f; // f suffix for float
    double pi_double = 3.14159265358979323846;
    long double pi_long_double = 3.14159265358979323846L; // L suffix for long double

    printf("Pi (float): %.10f\n", pi_float); // .10f for 10 decimal places
    printf("Pi (double): %.15lf\n", pi_double); // %lf for double
    printf("Pi (long double): %.20Lf\n", pi_long_double); // %Lf for long double

    printf("\n--- Character Value ---\n");
    char grade = 'A';
    printf("Grade: %c\n", grade); // %c for character

    printf("\n--- Boolean Value ---\n");
    // In C99, use #include <stdbool.h> and `bool` keyword.
    // In C23, `bool`, `true`, `false` are keywords by default.
    bool is_student = true;
    bool has_passed = false;
    printf("Is student? %d\n", is_student); // Prints 1 for true
    printf("Has passed? %d\n", has_passed); // Prints 0 for false

    // Printing max/min values from <limits.h> and <float.h>
    printf("\n--- System Limits ---\n");
    printf("Max int: %d\n", INT_MAX);
    printf("Min char: %d\n", CHAR_MIN);
    printf("Max float: %e\n", FLT_MAX); // %e for scientific notation

    return 0;
}

To compile and run this code:

# For C99/C11/C17 compilers
gcc -std=c17 data_types.c -o data_types

# For C23 compiler (if available and configured)
# The exact flag might vary, but often -std=c23 or -std=gnu23
# For GCC 13.x or later:
gcc -std=c23 data_types.c -o data_types

Then run:

./data_types

Note on sizeof and %zu: The sizeof operator returns the size in bytes. Its return type is size_t, which is an unsigned integer type. The correct format specifier for size_t in printf is %zu.

2.2 Variables

A variable is a named storage location in memory used to hold a value. Before you can use a variable, you must declare it.

2.2.1 Declaring Variables

When you declare a variable, you specify its data type and its name.

dataType variableName;

Examples:

int score;           // Declares an integer variable named 'score'
float price;         // Declares a float variable named 'price'
char initial;        // Declares a character variable named 'initial'
double temperature;  // Declares a double variable named 'temperature'

2.2.2 Initializing Variables

Initializing a variable means giving it an initial value when it is declared. If you don’t initialize a variable, it will contain a “garbage” value (whatever bits were previously in that memory location).

dataType variableName = initialValue;

Examples:

int score = 100;
float price = 99.99f; // Remember the 'f' for float literals
char initial = 'J';
double temperature = 25.5;
unsigned long count = 0UL;
bool is_active = true; // In C23, or use `_Bool is_active = 1;`

You can also declare multiple variables of the same type on one line:

int x, y, z;          // Declares three integer variables
int a = 10, b = 20; // Declares and initializes two integer variables

Important: In C, variables must be declared before they are used. Typically, declarations are placed at the beginning of a function or block, though C99 and later standards allow declarations almost anywhere.

2.2.3 Assigning Values to Variables

You can change the value of a variable at any point after it has been declared using the assignment operator =.

int num_students = 25; // Declaration and initialization
num_students = 30;     // Assignment: change the value

Code Example: Variables

#include <stdio.h>
#include <stdbool.h> // For bool, true, false in C99/C11/C17

int main() {
    // Declaring and initializing variables
    int student_id = 1001;
    char grade_letter = 'B';
    float average_score = 85.5f;
    double max_gpa = 4.0;
    bool is_enrolled = true; // true is 1, false is 0

    printf("Student ID: %d\n", student_id);
    printf("Grade Letter: %c\n", grade_letter);
    printf("Average Score: %.1f\n", average_score);
    printf("Maximum GPA: %.1lf\n", max_gpa);
    printf("Is Enrolled: %d\n", is_enrolled);

    // Reassigning values
    student_id = 1002;
    grade_letter = 'A';
    average_score = 92.3f;
    is_enrolled = false;

    printf("\n--- After Reassignment ---\n");
    printf("New Student ID: %d\n", student_id);
    printf("New Grade Letter: %c\n", grade_letter);
    printf("New Average Score: %.1f\n", average_score);
    printf("Is Enrolled now: %d\n", is_enrolled);

    // Declaring multiple variables
    int x, y, z;
    x = 10;
    y = 20;
    z = x + y;
    printf("x + y = %d\n", z);

    // Using C23 auto keyword for object definitions (limited type inference)
    // This allows you to omit the type name when initializing.
    // The type is deduced from the initializer.
    // Requires -std=c23 or equivalent compiler flag.
    #if __STDC_VERSION__ >= 202311L
    auto inferred_int = 42;
    auto inferred_float = 3.14f;
    auto inferred_char = 'K';
    printf("Inferred int: %d\n", inferred_int);
    printf("Inferred float: %.2f\n", inferred_float);
    printf("Inferred char: %c\n", inferred_char);
    #else
    printf("\n(C23 'auto' keyword for declarations not supported by current compiler standard)\n");
    #endif

    return 0;
}

Compile and Run:

# For older C standards, without C23 auto keyword
gcc variables.c -o variables

# For C23 compiler (if auto keyword is used)
gcc -std=c23 variables.c -o variables

Then run:

./variables

Exercise 2.1: Variable Declaration and Initialization

Declare variables to store the following information and initialize them with appropriate values:

  1. Your current age (integer).
  2. Your height in meters (floating-point, double).
  3. The first letter of your last name (character).
  4. Whether you have a driver’s license (boolean).
  5. The number of stars in a galaxy (unsigned long long, a very large number).

Then, print out the values of these variables using printf. Choose the correct format specifiers for each type.

Hint:

  • %d for int
  • %c for char
  • %lf for double
  • %d for bool (prints 0 or 1)
  • %llu for unsigned long long

2.3 Operators

Operators are special symbols that perform operations on variables and values (called operands). C has a rich set of operators.

2.3.1 Arithmetic Operators

These are used for mathematical calculations.

OperatorDescriptionExampleResult (if a=10, b=3)
+Additiona + b13
-Subtractiona - b7
*Multiplicationa * b30
/Divisiona / b3 (integer division)
%Modulo (remainder)a % b1

Important Note on Division:

  • When both operands of / are integers, C performs integer division, truncating any fractional part. 10 / 3 results in 3.
  • If at least one operand is a floating-point type, floating-point division is performed: 10.0 / 3 results in 3.333....

2.3.2 Assignment Operators

These are used to assign values to variables. The most basic is =, but there are shorthand compound assignment operators.

OperatorExampleEquivalent to
=x = 10
+=x += yx = x + y
-=x -= yx = x - y
*=x *= yx = x * y
/=x /= yx = x / y
%=x %= yx = x % y

2.3.3 Increment and Decrement Operators

These are unary operators (++ and --) used to increase or decrease a variable’s value by 1.

  • Prefix (++x, --x): The operation is performed before the value is used in the expression.
  • Postfix (x++, x--): The operation is performed after the value is used in the expression.
OperatorDescriptionExampleResult (if x=5)
++Increment by 1x++x becomes 6 (value used is 5)
++xx becomes 6 (value used is 6)
--Decrement by 1x--x becomes 4 (value used is 5)
--xx becomes 4 (value used is 4)

Code Example: Operators

#include <stdio.h>

int main() {
    int a = 20;
    int b = 7;
    int result_int;
    double c = 20.0;
    double d = 7.0;
    double result_double;

    printf("--- Arithmetic Operators ---\n");
    result_int = a + b;
    printf("%d + %d = %d\n", a, b, result_int); // 20 + 7 = 27

    result_int = a - b;
    printf("%d - %d = %d\n", a, b, result_int); // 20 - 7 = 13

    result_int = a * b;
    printf("%d * %d = %d\n", a, b, result_int); // 20 * 7 = 140

    result_int = a / b;
    printf("%d / %d = %d (Integer Division)\n", a, b, result_int); // 20 / 7 = 2 (truncates)

    result_double = c / d;
    printf("%.1lf / %.1lf = %.2lf (Floating Point Division)\n", c, d, result_double); // 20.0 / 7.0 = 2.86

    result_int = a % b;
    printf("%d %% %d = %d (Modulo)\n", a, b, result_int); // 20 % 7 = 6

    printf("\n--- Assignment Operators ---\n");
    int x = 10;
    printf("Initial x = %d\n", x);

    x += 5; // x = x + 5;
    printf("x after x += 5: %d\n", x); // x is 15

    x -= 3; // x = x - 3;
    printf("x after x -= 3: %d\n", x); // x is 12

    x *= 2; // x = x * 2;
    printf("x after x *= 2: %d\n", x); // x is 24

    x /= 4; // x = x / 4;
    printf("x after x /= 4: %d\n", x); // x is 6

    x %= 5; // x = x % 5;
    printf("x after x %%= 5: %d\n", x); // x is 1

    printf("\n--- Increment/Decrement Operators ---\n");
    int i = 5;
    int j = 5;
    int k;

    printf("Initial i = %d, j = %d\n", i, j);

    // Postfix increment: use then increment
    k = i++;
    printf("k = i++ (k = %d, i = %d)\n", k, i); // k = 5, i = 6

    // Prefix increment: increment then use
    k = ++j;
    printf("k = ++j (k = %d, j = %d)\n", k, j); // k = 6, j = 6

    // Postfix decrement
    k = i--;
    printf("k = i-- (k = %d, i = %d)\n", k, i); // k = 6, i = 5

    // Prefix decrement
    k = --j;
    printf("k = --j (k = %d, j = %d)\n", k, j); // k = 5, j = 5

    return 0;
}

Compile and Run:

gcc operators.c -o operators
./operators

Exercise 2.2: Calculations with Operators

Write a C program that performs the following calculations and prints the result for each:

  1. Calculate the area of a rectangle with length 15 and width 7.
  2. Calculate the remainder when 200 is divided by 13.
  3. Given an initial score of 75, increase it by 10, then double the result, and finally decrease it by 5. Print the score at each step.
  4. Declare a double variable temperature and set its value to 28.5. Increment it by 1.5 using an assignment operator. Print the final temperature.

Example for (1): Area = length * width


Exercise 2.3: Data Type Conversions (Mini-Challenge)

What will be the output of the following C code snippets? Try to predict before running them. Explain why.

Snippet A:

#include <stdio.h>

int main() {
    int num1 = 17;
    int num2 = 5;
    float result = num1 / num2;
    printf("Result A: %.2f\n", result);
    return 0;
}

Snippet B:

#include <stdio.h>

int main() {
    int num1 = 17;
    int num2 = 5;
    float result = (float)num1 / num2; // Type cast num1 to float
    printf("Result B: %.2f\n", result);
    return 0;
}

Snippet C:

#include <stdio.h>

int main() {
    char ch = 'X';
    printf("Character: %c, ASCII Value: %d\n", ch, ch);
    return 0;
}

Explanation:

  • Type Casting: In C, you can explicitly convert one data type to another using a process called type casting. You put the desired type in parentheses before the variable or expression you want to convert, like (dataType)expression. This is important when you want to force floating-point division, for example.
  • Implicit Conversions: C also performs implicit type conversions (coercion) in certain situations, such as when assigning a smaller type to a larger type, or during mixed-type arithmetic operations. In arithmetic, the “smaller” type is usually promoted to the “larger” type to prevent loss of data.

This chapter covered the absolute basics of data storage and manipulation in C. You now understand different data types, how to declare and assign values to variables, and how to perform fundamental operations using C’s rich set of operators. In the next chapter, we’ll learn how to make our programs make decisions and repeat actions using control flow statements.