CODE: C CPP Variables, Identifiers, and Data Types

Variables are the foundation of any C++ program—they allow you to store, modify, and process data.

Understanding how variables work, how data is represented, and how types affect behavior, memory, and correctness is foundational to writing safe, efficient, and scalable C and C++ programs.

At low levels, variables are simply named views over memory.

At higher levels, they become tools for abstraction, safety, and design.

This article provides a deep, structured explanation covering:

  • Variables and identifiers
  • C and C++ basic syntax (side-by-side where relevant)
  • Primary (built-in) vs user-defined types
  • Data types vs data modifiers
  • Pointers vs references
  • Storage duration and qualifiers
  • Initialization models (classic → modern)
  • Practical _when & why_ guidance
  • A complete comparison and summary table

Mental Model (Read This First)

Before syntax, understand this:

A variable is **not a value**.
A variable is a **name bound to a memory location**, interpreted through a **type**.

The type defines:

  • How many bytes are used
  • How bits are interpreted
  • What operations are legal
  • How the compiler optimizes access

Everything that follows builds on this idea.


Variables and Identifiers (C and C++)

What Is a Variable?

A variable is a named storage location whose value can change during program execution.

C and C++ Syntax

cpp
int age;          // declaration
age = 25;         // assignment
int score = 90;   // initialization

When: Any time you need to store state

Why: Programs are state machines; variables represent state


Identifiers (Naming Rules)

An identifier is the name you give to a variable, function, or type.

Rules (C & C++):

  • Letters, digits, underscore (_)
  • Cannot start with a digit
  • Case-sensitive
  • Must not be a keyword

cpp
int maxScore;   // valid
int _count;     // valid but discouraged
int 2value;     // invalid

Why naming matters:

Identifiers carry _semantic meaning_. Bad names cause bugs even when code is correct.


Data Types — The Big Picture

All data types fall into two major categories:

1.Primary (Built-in) Types

Provided directly by the language and ABI.

  • Integers
  • Floating-point
  • Boolean
  • Character
  • Void

2.User-Defined Types

Composed from primary types.

  • Arrays
  • Structs
  • Unions
  • Enums
  • Classes (C++ only)
  • Type aliases

We’ll cover each in C first, then extend to C++.


Primary (Primitive) Data Types in C

TypeExampleWhenWhy
charchar c = 'A';Characters, bytesSmallest addressable unit
unsigned charunsigned char u = 255;Raw dataFull byte range
intint n = 100;Counters, logicNatural CPU word
unsigned intunsigned int u = 300;Non-negative valuesExtended range
shortshort s = 32000;Small integersMemory saving
longlong l = 1000000;Large integersWider range
floatfloat f = 3.14f;Approx decimalsFast, low memory
doubledouble d = 3.14159;Precision decimalsAccuracy
long doublelong double ld;Scientific mathMaximum precision
_Bool_Bool flag = 1;Boolean logicExplicit intent
voidvoid func()No valueType safety

Integer Types:

  • int: Used to store whole numbers (e.g., 10, -5).
  • short: Used to store smaller whole numbers.
  • long: Used to store larger whole numbers.
  • long long: Used to store very large whole numbers.
  • unsigned int: Used to store positive whole numbers only.

Floating-Point Types:

  • float: Used to store decimal numbers with single precision.
  • double: Used to store decimal numbers with double precision.

Difference between float and double:

The precision of a floating-point number is the number of digits that can be stored after a decimal point. A float can store seven digits after a decimal point precisely. Whereas, double can store 15 digits after a decimal point precisely. It is recommended to use double for floating-point values.

Character Types:

  • char: Used to store single characters (e.g., 'a', '5', '$').

Boolean Type:

  • bool: Used to store either true or false values.
- **bool** is supported as built in data type from c++ 98 and above.
- _Bool for C language from c99 by "stdbool.h" library

Void type:

  • void: Represents the absence of a type or value.
Neither C/C++ has a **string** type, they have provisions for handling strings of characters.
- C using the array of char - char str[] = "Hello";
- C++ using the string class from the STL - std::string str = "Hello";

Pointers

pointer is a reference to an object of a given type, the pointer itself typically holds the address of the object it points to.

  • the type of the pointer is used as the type when it's dereferenced,
  • and also determining the size of increments and decrements operations on the pointer.

cpp
int I = 10;
int * i =  &I;
*i = 50; // Dereference the pointer then the I will be 50

// pointer examples
// pointer to constant int, 
// so the int is a constant value, 
// but the pointer is not, 
// so it can point to any other value.
const int a = 10;
const int * ptr = &a;
// or
int const * ptr = &a;
*ptr = 5; // is wrong
ptr++;    //is allowed

// const ptr to int, 
// so the pointer is constant 
// so you can't change the pointer after declaration.
int a = 10;
int *const ptrt = &a;
*ptr = 5;   // is allowed
ptr++;      // is wrong

// pointer to function
int function(int);
int (*pFunc)(int) = function;
// the function name is an address for the function.
// to call it
int i = (*pFunc)(5);


User-Defined Types in C

Arrays (Contiguous Memory)

c
int numbers[5] = {1,2,3,4,5};

When: Fixed-size collections

Why: Cache-friendly, fast indexed access


Structures (`struct`)

c
struct Person {
    char name[50];
    int age;
};

struct Person amr = {"Amr", 30};

When: Group related data

Why: Data organization without overhead


Unions (Shared Memory)

c
union Data {
    int i;
    float f;
    char c;
};

When: Memory-constrained systems

Why: All members share the same memory

Only one member is valid at a time.

Enumerations (`enum`)

c
enum Color { RED, GREEN, BLUE };

When: States, modes, options

Why: Self-documenting integer values


C++ Extensions and Enhancements

C++ inherits all C types, then builds on them.

Boolean Type

cpp
bool flag = true;

Why better than C:

Readable, type-safe, semantic


Identifiers

Same rules as C, but C++ also allows namespaces to avoid collisions.

cpp
namespace MyApp {
    int score;
}

When: Large projects with multiple files.

Why: Avoids name conflicts.


References (C++ Only)

cpp
int x = 10;
int& ref = x;
ref = 20;

A reference is very much like a pointer but with different semantics

  • the major distinction between pointers and references is that references are immutable which means they can not change once it is defined.
  • and references are accessed as aliases
  • You can't reference a reference () it will simply copy the value of the new reference to the old one.
  • You can't have a pointer to a reference.
  • You can't have an array of references.
  • That's because references are not objects and don't occupy the memory so doesn't have the address. You can think of them as the aliases to the objects. Declaring an array of nothing has not much sense.

When: Function parameters, aliases

Why: Safer than pointers, cannot be null


Structs in C++ (Upgraded)

cpp
struct Person {
    std::string name;
    int age;
    void print() const {
        std::cout << name << " " << age;
    }
};

Same keyword, far more powerful.

`struct` members are `public` by default

Classes (C++ Only)

cpp
class Circle {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    double area() const {
        return 3.14159 * radius * radius;
    }
};

When: Object-oriented design.

Why: Encapsulation, abstraction, and reusable code.

`class` members are `private` by default

Type Aliases

The C way

c
typedef char* string_t;
typedef long int real_number_t;

This will define a new data type called string_t and real_number_t

The C++ way

cpp
using string_t = char*;
using real_number_t = long int;

C++ can use typedef too like normal C syntax

When: Simplify type names.

Why: Readability and maintainability.


Type Qualifiers (Change Behavior)

QualifierMeaning
constCannot modify
volatilePrevent optimization
mutableModifiable in const objects
  • const: which makes the variable immutable, so its value can not be changed.
  • volatile: which marks an object that may be changed by another process, this is commonly used for threaded and multi-user applications that share memory.
  • mutable: this is used to modify a data member from a const qualified member function. (only C++)

const

In C++, we can use the const keyword to declare a constant.

Declares a variable as constant. It means the variable's value cannot be changed once it's initialized. The basic syntax for creating a constant is:

cpp
// const data_type identifier = const_value;
const int number = 10;

In C/C++, you have to initialize a constant at the time of its declaration. If you don’t initialize a constant at the time of creating it, an error will occur.

volatile

Indicates that a variable's value can be changed unexpectedly by external sources (hardware, threads, etc.). It prevents the compiler from optimizing code involving that variable.

cpp
volatile int sensorValue = 0;

mutable

Applies to class members. It allows a member of an object to be modified even if the object is declared as const.

cpp
class Example { 
	public: 
		mutable int counter; // Mutable variable within a const object 
		// ... 
};


Storage Class Specifiers

StoragePurpose
staticPersistent lifetime
externCross-file linkage
registerHint for speed (obsolete)
autoType deduction (C++ only)
  • static: objects stored in a static memory have a life beyond the execution of a block, it is commonly used for keeping state between usages of a given function.
  • register: to store the processor register, it is a suggestion for the compiler, maybe use it or not.
  • extern: extern variables are defined in a separate translation unit and are linked together by the linker.
  • auto: this is the default storage class.

static

Alters the lifetime and visibility of a variable. It preserves the variable value between function calls and is accessible only within the current scope.

The static storage class tells the compiler to maintain the value of the variable throughout the lifetime of the program. Static variables are similar to the local variables but are preceded by a static keyword.

cpp
void exampleFunction() { 
	static int num = 0; 
	// Static variable retains its value between function calls 
	// ... 
}

register

Suggests to the compiler that a variable should be stored in a CPU register for faster access. However, modern compilers often optimize this automatically.

cpp
register int count;

extern

Used to declare a variable that is defined in another translation unit (typically another source file). It allows multiple files to access the same variable.

cpp
// In File1.cpp
extern int sharedValue; // Declaration 

// In File2.cpp 
int sharedValue = 10; // Definition

auto

The storage class “Auto” is applied to the local variables and is automatically assigned by the compiler to local variables. Local variables preceded by the ‘auto’ keyword remain active in the function in which they are declared and go out of scope once the function exits.

Automatically deduces the data type of a variable from its initializer.

cpp
auto value = 10; // Deduces value as an int


Initialization

Assignment Initialization

This is the traditional way of initializing variables. You assign a value to a variable using the = operator.

cpp
int x = 10;
double y = 3.14;

  • It provides a clear and familiar way to assign values.
  • It allows implicit type conversions, which can be useful but also potentially risky if unintended conversions occur.

Brace/List Initialization

Brace initialization, introduced in C++11, is a uniform way of initializing variables. It helps prevent certain types of errors, such as narrowing conversions.

cpp
int x{10};
double y{3.14};
std::string name{"Alice"};
std::vector<int> numbers{1, 2, 3, 4};

  • Prevents Narrowing Conversions: Brace initialization will trigger a compile-time error if a narrowing conversion is detected (e.g., int x{3.14}; will fail).
  • Uniform Syntax: It provides a uniform way to initialize all types, from simple data types to complex objects.
  • Consistency and Safety: It reduces the likelihood of bugs by enforcing stricter type checks and avoiding unintentional type conversions.

3. Key Differences Between C and C++

FeatureCC++When & Why
Boolean_Bool flag = 1;bool flag = true;C++ bool is more readable
Stringschar str[] = "Hi";std::string str = "Hi";C++ simplifies string handling
Structsstruct { int x; }struct { int x; void f(){}; };Methods inside structs in C++
Function OverloadingN/Aint f(int); double f(double);Reuse names for different types
Default ArgumentsN/Avoid f(int x=5);Simplifies calls
ReferencesN/Aint& r = x;Safer aliasing
NamespacesN/Anamespace A{}Avoid collisions
OOPN/AClasses, inheritance, encapsulationModel real-world objects

When to Use C: Embedded, low-level, hardware, performance-critical

When to Use C++: Applications, OOP design, high-level abstractions, large-scale projects


4. Summary Table of Usage

ConceptExampleCC++WhenWhy
Variableint x = 5;Dynamic dataCore logic
Identifierint score;AlwaysClarity
Integer typesint, short, longCountingMemory optimization
Float typesfloat, doubleDecimalsPrecision
Boolean_BoolboolLogical flagsReadability
CharcharTextMemory-efficient
Voidvoid func(){}No returnType safety
Pointerint* p;Dynamic memoryPerformance
ReferenceN/Aint& rFunction aliasSafety
Arrayint arr[5];ListsFast access
StructstructObjectsOrganize data
UnionunionMemory-criticalSave memory
EnumenumStatesReadability
ClassN/AclassOOPEncapsulation
Modifiers/Qualifierslong, const, etc.Number controlType safety
Storage Classstatic, externScope/lifetimePersistent variables