int count = 3; and std::string label = "three"; can describe the same quantity, but they let you do different things. You can add one to count; you can append text to label. A type tells the compiler which operations make sense, how a value is represented, and which mistakes it can catch before the program runs.
A type, such as int, describes a value and its permitted operations. A data structure, such as a vector of integers, organizes values for a particular use. The distinction is useful, though the categories overlap in C++: a vector has a type of its own, and you can define types that group several pieces of data.
Start with values, not storage sizes
C++ has built-in types for common kinds of values. bool holds true or false. char holds a character-sized value, while int is a common choice for whole-number arithmetic. double represents floating-point numbers, including approximations of fractions. For text with multiple characters, you will usually use std::string, a standard-library type rather than a built-in one.
A declaration gives a value a name and a type. Initialize local variables when you declare them so you do not accidentally use an uninitialized value:
int attempts{0};
bool connected{false};
double temperature{21.5};
char grade{'A'};
std::string username{"Mira"};
Code using std::string needs #include <string>. Braces are helpful while learning because they reject some conversions that discard information, such as int whole{3.7};. They do not check whether an incoming value is within an acceptable range or makes sense for your program.
Do not assume int has the same size on every platform. Its minimum range is specified, but its actual size depends on the implementation. An unsigned type may seem like an obvious choice for a count that cannot be negative, yet mixing signed and unsigned arithmetic can give surprising results. Keep related arithmetic in compatible types, and check that input fits before converting it. If a file format or protocol genuinely requires a fixed-width integer, std::int32_t from <cstdint> states that requirement explicitly on implementations that provide it.

Floating-point values are approximations
A double works well for measurements, but many decimal fractions cannot be represented exactly in binary floating-point. A calculated measurement might therefore fail an exact comparison with 0.3, even if it displays as 0.3. For money, one simple approach is to store whole minor units, such as cents, in an integer type after checking that possible totals fit its range. For measurements, use a comparison tolerance appropriate to the scale and requirements of the calculation.
Make conversions visible
C++ sometimes converts values automatically. In double average = total / count;, if total and count are both integers, integer division happens before the result becomes a double. A total of 7 and a count of 2 produce 3.0, not 3.5. Convert an operand before dividing:
int total{7};
int count{2};
double average = static_cast<double>(total) / count;
You still need to check that count is not zero. A cast changes the type used for an operation; it does not make invalid input valid. As you work through these examples, pay attention to compiler warnings about narrowing, signedness, and unused values.
Choose a container by how values are used
Separate variables are fine for a small, fixed set of unrelated facts. When you have repeated values, a container is usually easier to work with. The standard library offers several choices:
std::array<T, N>: Holds a fixed number of elements of typeT. Its size is part of its type, so it fits a count known at compile time, such as seven daily readings.std::vector<T>: Holds an ordered sequence that can grow or shrink. It is a practical default when the list length is determined while the program runs.std::map<Key, Value>: Associates unique keys with values and keeps the keys ordered. Use it when lookup by a meaningful key matters more than access by numeric position.
For example, std::vector<int> scores{8, 10, 7}; holds three integers in order, and scores.push_back(9); adds a fourth. scores[0] accesses the first element, but the subscript operator does not check bounds. During early exercises, scores.at(0) can be more informative: it throws an exception if the index is invalid. Either way, you must consider whether the element exists.
Choose std::array when a fixed count is part of the problem, not just because your first sample happens to have that many items. A vector can start empty and grow. For both containers, .size() gives the current element count, not the index of the last element. An empty container has no last element.

Give related values a name with a struct
A container collects values of one element type. A struct groups fields that belong to one concept, even when those fields have different types. Here is a student's practice result:
struct Result {
std::string name;
int correct{0};
int questions{0};
};
Result practice{"Mira", 8, 10};
practice.correct makes the number's meaning clearer than a standalone variable named x. The field initializers supply defaults when a Result is created without explicit counts. They do not enforce a rule such as correct <= questions; code that accepts or constructs a result must check it.
You can keep multiple records in std::vector<Result> results;. That is generally easier to keep consistent than separate vectors for names, correct answers, and question totals. Inserting or removing a record keeps its fields together. A struct is also a type, so you can pass a record to a function or return it as one meaningful value.
When an enum fits better than an integer
Some values describe a small set of named states rather than a quantity. An enum makes that clear:
enum class Status { pending, passed, failed };
Status current{Status::pending};
Status::passed communicates more than an integer whose states are documented as 0, 1, and 2. An enum class also avoids implicit conversion to an integer in ordinary expressions. It cannot decide what to do with an unexpected value read from a file, though. Validate external data before translating it into a status.
Distinguish data shape from program control
Nested containers can model data with multiple dimensions. std::vector<std::vector<int>>, for example, can hold rows of integers. Rows may have different lengths, so knowing how many rows you have does not tell you whether a particular column exists. If every row must have exactly three entries, std::vector<std::array<int, 3>> expresses that requirement in the type.
Match the shape to the facts. A student's name and score can live in a small struct. For any number of students, put those records in a vector. If lookup by unique student ID is the main task, a map keyed by ID may be a better fit. The most useful structure is the one that makes mistaken assumptions easier to spot, not the most elaborate one.
For practice, define struct Reading { std::string sensor; double celsius; }; and create a std::vector<Reading> with two entries. Print each sensor name beside its measurement, then use readings.size() to report the count. Keep the temperature as double when printing it: converting it to int would discard the fractional part.
