optional · CSS study notes
II.8 Program Structure & Data Types
⬆️ P-I.II. Programming Fundamentals · Question Bank · Chapter II (2016–2026)
🧠
Remember: Type decides the values; scope decides the name; lifetime decides the object.
Start here — what you should be able to do
- Read a complete program and identify declarations, definitions and initialization.
- Choose types and explain conversion/integer-division pitfalls.
- Distinguish scope, storage duration and object lifetime.
Reading order: foundation checks → full notes and examples → exam traps → self-test without notes → past-paper answers. All sections below remain part of the topic; code examples are kept in full.
Foundation checks
Essential distinctions
| Concept | Example | Meaning |
|---|---|---|
| Declaration | extern int count; | Introduces a name and type; need not allocate storage. |
| Definition | int count = 0; | Defines the object. |
| Initialization | int x = 3; | Gives the object its initial value. |
| Assignment | x = 4; | Changes an existing object. |
intis not guaranteed to occupy four bytes; usesizeofand the implementation's limits.- Both implicit and explicit conversions can lose information.
- Cast before division:
static_cast<double>(sum) / count, with count nonzero. typedef/usingcreate aliases, not distinct new types.- Initialize ordinary local variables before reading them.
Sources: C++ types, C++ conversions.
1. Anatomy of a Program (C/C++)
#include <stdio.h> // 1. Preprocessor directives
#define MAX 100 // (headers, macros)
int globalCount = 0; // 2. Global declarations
int square(int n) { // 3. Function prototypes/definitions
return n * n;
}
int main() { // 4. main() — program entry point
int x = 5; // local declarations
x = square(x); // statements/processing
printf("%d", x); // output
return 0; // 5. return — exit status to OS
}
- Preprocessor directives —
#include,#define, processed before compilation - Global declarations — variables/constants visible to the whole file
- Function prototypes and definitions — including
main(), the mandatory entry point main()body — local declarations, statements, control flow- Return statement — sends an exit status back to the operating system (
0= success by convention)
2. Data Types
Primitive (built-in) types:
| Type | Typical Size (C/C++) | Holds |
|---|---|---|
| int | 4 bytes | Whole numbers |
| float | 4 bytes | Single-precision decimal |
| double | 8 bytes | Double-precision decimal |
| char | 1 byte | Single character |
| bool (C++) | 1 byte | true/false |
| void | — | No value / empty type |
- Type modifiers:
signed,unsigned,short,longadjust range/sign (e.g.unsigned int,long long)
Derived/composite types: array, pointer, function
User-defined types: struct, union, enum, class (C++), typedef-based aliases
3. Variables: Declaration, Scope, Lifetime
- Declaration:
int age;binds an identifier to a data type and reserves memory for it - Initialization:
int age = 25;declares and assigns a value in one step - Scope — where in the code a variable name is visible: local (inside a function/block) vs global (whole file/program)
- Lifetime — how long a variable's memory persists: automatic (created/destroyed with each function call, default for locals) vs static (persists for the entire program run) vs dynamic (programmer-controlled via
malloc/newandfree/delete)
4. Type Conversion
- Implicit (automatic) conversion: the compiler converts one type to another automatically, e.g.
int→floatin a mixed expression (widening, generally safe) - Explicit conversion (casting): the programmer forces a conversion, e.g.
(int) 3.99→3(may lose data — narrowing)
5. 💻 Code Examples: C & C++
The same program anatomy (preprocessor → globals → functions → main → return) with the full primitive type set, plus C++'s extra bool type and implicit/explicit conversion:
C:
#include <stdio.h> // 1. Preprocessor directive
#define MAX 100
int globalCount = 0; // 2. Global declaration
int square(int n) { // 3. Function definition
return n * n;
}
int main(void) { // 4. main() -- entry point
int x = 5; // local declaration
unsigned long big = 100000UL;
float pi = 3.14f;
char grade = 'A';
x = square(x);
float avg = 7 / 2; // implicit conversion: int -> float, but 7/2 truncates to 3 first
int rounded = (int) 3.99; // explicit conversion (cast): -> 3, decimal part lost
printf("%d %c %.1f %d\n", x, grade, avg, rounded);
return 0; // 5. return
}
C++:
#include <iostream>
using namespace std;
#define MAX 100
int globalCount = 0;
int square(int n) {
return n * n;
}
int main() {
int x = 5;
bool passed = true; // C++-only primitive type (true/false)
double pi = 3.14159;
char grade = 'A';
x = square(x);
double avg = 7.0 / 2; // implicit conversion, no truncation this time
int rounded = static_cast<int>(3.99); // explicit conversion, C++ style cast
cout << x << " " << grade << " " << avg << " " << rounded
<< " " << boolalpha << passed << endl;
return 0;
}
6. MCQ Traps
- main: normal hosted C/C++ application control reaches main through runtime startup; initialization may happen beforehand. main's position in the source file does not determine execution order.
- Declaration versus initialization: a declaration introduces a name/type; a definition supplies the entity. An ordinary uninitialized local must not be read before receiving a valid value.
- Implicit conversion from
floattointtruncates the decimal part; it does not round voidis a valid type for "no return value" or "no parameters," but a variable cannot be declared of typevoid- Automatic (stack) variables lose their value when the function returns; static variables retain their value across calls
7. ✅ Self-Test Questions (with Answers)
Q1. List, in order, the standard sections of a C/C++ program.
Answer — key points
- A conventional C/C++ source file is laid out in five sections, in this order:
- Preprocessor directives —
#includefor headers and#definefor macros; processed before compilation begins - Global declarations — global variables, constants and function prototypes, visible throughout the file
- Function definitions — the bodies of the user-defined functions
main()function — the mandatory entry point, containing local declarations, processing statements and output- Return statement —
return 0;sends an exit status to the operating system, where 0 conventionally means success
#include <iostream> // 1. Preprocessor directives
#define MAX 100
using namespace std;
int globalCount = 0; // 2. Global declarations
int square(int n); // function prototype
int square(int n) { // 3. Function definition
return n * n;
}
int main() { // 4. main() -- entry point
int x = 5; // local declaration
x = square(x); // processing
cout << x << endl; // output -> 25
return 0; // 5. return exit status to the OS
}
- A prototype in section 2 lets
main()call a function whose body appears later in the file; without it the compiler reports "not declared in this scope" - Sections 3 and 4 may be interchanged, provided prototypes are present — the order above is convention, not a compiler requirement
- main: normal hosted C/C++ application control reaches main through runtime startup; initialization may happen beforehand. main's position in the source file does not determine execution order.
Q2. What is the difference between a primitive and a derived data type? Give one example of each.
Answer
- Fundamental types: language-provided types such as integers, floating-point types, Boolean and void.
- Compound/derived forms: arrays, pointers, references and function types are formed using other types.
- Class/enum types: structs, classes, unions and enumerations provide additional modelling tools.
- Alias:
typedefandusingname an existing type; they do not by themselves create a distinct new type. - C versus C++: category names and supported forms differ; C does not have C++ reference types.
- Example:
intis fundamental;int[5]is an array type;int*is a pointer type.
| Category | Built from | Holds | Examples |
|---|---|---|---|
| Primitive | The language itself | A single atomic value | int, float, double, char, bool, void |
| Derived | Existing types | A collection of, or reference to, values | array, pointer, function, reference |
| User-defined | Programmer's own definition | A named composite of members | struct, union, enum, class, typedef |
// PRIMITIVE: one atomic value each
int age = 25; // 4 bytes
char grade = 'A'; // 1 byte
double pi = 3.14159; // 8 bytes
// DERIVED: built out of the primitive int
int marks[5] = {80, 75, 90, 60, 88}; // array -> 5 ints, 20 bytes
int *p = &age; // pointer-> holds an address
int square(int n); // function-> derived type too
// USER-DEFINED: a composite the programmer names
struct Student { int roll; char grade; double gpa; };
cout << sizeof(age) << endl; // 4
cout << sizeof(marks) << endl; // 20 (5 * 4)
- MCQ trap:
voidis a primitive type but no variable can be declared of typevoid— it means "no value", used for a function's return type or an empty parameter list (void*is different: a generic pointer). - Examiners also test that an array is a derived type, not a primitive one, and that a function counts as a derived type in the standard C classification, which surprises most candidates.
Q3. Differentiate scope from lifetime using a local variable as an example.
Answer
- Scope: where a name can be used in source code.
- Storage duration: how long storage is available.
- Object lifetime: when an object exists in that storage; construction/destruction can make this more specific than storage duration.
- Ordinary local: normally automatic storage; its lifetime ends when its block exits.
- Static local: block scope, persistent storage across calls.
- Dynamic object: can outlive the local pointer that first referred to it.
- Implementation: “stack” and “data segment” describe common implementations, not universal language requirements.
#include <iostream>
using namespace std;
int g = 100; // global: file scope, static lifetime
void counterDemo() {
int autoVar = 0; // block scope, AUTOMATIC lifetime -> reset every call
static int stat = 0; // block scope, STATIC lifetime -> survives calls
autoVar++; stat++;
cout << autoVar << " " << stat << endl;
}
int main() {
counterDemo(); // 1 1
counterDemo(); // 1 2
counterDemo(); // 1 3 <- stat remembers; autoVar does not
// cout << autoVar; // ERROR: out of scope here
return 0;
}
| Variable kind | Scope | Lifetime | Stored in |
|---|---|---|---|
| Local (automatic) | Its block only | Until the block exits | Stack |
| Static local | Its block only | Whole program run | Data segment |
| Global | Whole file / program | Whole program run | Data segment |
| Dynamic (new/malloc) | Wherever the pointer reaches | Until delete/free is called | Heap |
- MCQ trap: a
staticlocal has block scope but program lifetime — the classic "which of these keeps its value between calls?" - question.
- Note also the reverse mismatch: memory from
newhas a lifetime that outlives the scope of the pointer that created it, and forgettingdeletecauses a memory leak. - Returning the address of a local (
return &x;) yields a dangling pointer, because the scope ended and the lifetime ended together.
Q4. What is the difference between implicit and explicit type conversion?
Answer
- Implicit conversion: applied without an explicit cast, according to the language's conversion rules.
- Explicit conversion: requested with a cast, such as
static_cast<double>(sum). - Information loss: either can lose range or precision; implicit does not mean safe or lossless.
- Integer division:
double x = 5 / 2;gives 2.0 because division happened first. - Correct form:
double x = 5.0 / 2;gives 2.5. - Floating to integer: representable values are truncated toward zero; range must be valid.
- Avoid: a universal “char → short → int → long → float → double” safety ladder.
| Aspect | Implicit Conversion | Explicit Conversion (Cast) |
|---|---|---|
| Performed by | The compiler, automatically | The programmer |
| Syntax | None; happens silently | (int)x or static_cast<int>(x) |
| Direction | Usually widening | Often narrowing |
| Data loss | Usually none | Possible and expected |
| Example | double d = 5; | int n = (int)3.99; |
int a = 7, b = 2;
double d;
d = a / b; // 3.0 -- TRAP: int/int = 3 FIRST, then widened
d = (double)a / b; // 3.5 -- cast forces real division
d = a / 2.0; // 3.5 -- 2.0 is a double, so a is promoted
int n = (int)3.99; // 3 -- TRUNCATES, does not round
int m = static_cast<int>(-3.99); // -3 -- truncates toward zero
char c = 'A';
int k = c + 1; // 66 -- 'A' implicitly promoted to its ASCII 65
Conversion trap: a representable floating-to-integer conversion truncates toward zero. Use an appropriate rounding function rather than assuming x + 0.5 handles every sign and case; cast before integer division when a fractional result is needed.
Q5. Why does declaring int x; without initialization still "work" but produce unpredictable output if used before assignment?
Answer
- Declaration/definition:
int x;defines an ordinary local object without giving it a usable initial integer value. - Initialization:
int x = 0;establishes a known value immediately. - Reading too early: do not predict a “garbage” number; an uninitialized read is not a valid way to obtain a value.
- Size: use
sizeof(int); four bytes is common, not guaranteed. - Practice: initialise counters and accumulators and enable compiler warnings.
- Contrast: static-storage objects have zero-initialization rules that ordinary automatic locals do not share.
#include <iostream>
using namespace std;
int counter; // GLOBAL/static -> automatically zero-initialised to 0
int main() {
int x; // LOCAL (automatic) -> garbage, NOT zero
int y = 0; // safely initialised
cout << counter; // always 0
cout << x; // unpredictable: 0, 32767, -858993460, ...
x = 10; // from here on x is well defined
cout << x; // 10
return 0;
}
Accumulator rule: initialise the running total before the first addition.
int sum; // BUG: starts at garbage
for (int i = 1; i <= 5; i++) sum += i;
cout << sum; // should be 15; prints garbage + 15
// FIX: int sum = 0;
MCQ trap: global and static variables ARE zero-initialised automatically; local (automatic) variables are NOT. This asymmetry is the single most-tested point here. Always initialise counters and accumulators at declaration — and compile with -Wall so the warning is not missed.
8. 📜 Past-Paper Questions & MCQs (CSS 2016–2026)
2019 · Paper-I · Q3(c) · 5 marks
Recall cue: Explain preprocessing; give include, macro and conditional-compilation examples.
Q. What is the role of preprocessor directives? Give three examples in C++.
Answer — key points
- Role: preprocessor directives are instructions beginning with
#that are handled before compilation. They include files, define macros and constants, and select which code is compiled. #include: inserts a header file, e.g.#include <iostream>.#define: defines a macro or constant, e.g.#define PI 3.14159.#ifndef / #define / #endif: conditional compilation and include guards, which stop a header from being included twice.- Others:
#undef,#ifdef,#pragma once.
MCQs
2025 P-I · Q6. Which of the following is the false statement in the C language?
- (A)
static a = 10; - (B)
static int func (int); - (C)
static static int a;✅ - (D) None of these
- Answer: (C)
static static int a;. - Why: a declaration may have only one storage-class specifier, so repeating
staticis a compile error. (A) is accepted by older C compilers (the type defaults to int), and (B) is a valid declaration of a function with internal linkage.
Recall check and coverage
- Explain the three learning outcomes at the top without looking.
- Reproduce one example or trace and test a boundary case.
- Answer each self-test, then compare with the key.
- Attempt each recorded past-paper task on this page.
Recorded written references: 2019 · Paper-I · Q3(c) · 5 marks
Evidence limit: this page preserves the question records found in your Notion notes. Disputed labels and questions not checked against original PDFs remain flagged in the Question Bank; this is not a claim of exhaustive original-paper verification.