Lecture 1: Variables
Prerequisites: Introduction (programme structure,
printf,scanf, compilation workflow).
1. Overview
- This chapter addresses the question of how C programmes store, name, and manipulate data through the mechanisms of variables, data types, constants, and operators.
- Upon completion, you will be able to declare variables of appropriate types, perform arithmetic with correct precedence, define constants, and apply both implicit and explicit type conversions.
2. Learning Outcomes
Upon completing this chapter, you should be able to:
- Distinguish between variables and constants, and explain the role each plays in a C programme.
- Apply the rules for naming identifiers and follow established naming conventions.
- Select appropriate data types (
int,float,double,char) based on the nature of the data to be stored, and describe their typical sizes and value ranges. - Declare, assign, and initialise variables correctly, understanding the memory model that underpins each operation.
- Use the five arithmetic operators (
+,-,*,/,%) and evaluate expressions according to the rules of operator precedence and associativity. - Define constants using both
#defineandconst, and articulate the differences between the two approaches. - Explain implicit type conversion (promotion) and perform explicit type casting, recognising situations in which data loss may occur.
3. Key Terms
| Term (EN) | 中文 | Definition / Notes |
|---|---|---|
| Variable | 变量 | A named storage location in memory that holds a value which may change during programme execution. Every variable has a type, a name, an address, and a value. |
| Constant | 常量 | A value that cannot be altered after it is defined. May be established via #define (preprocessor macro) or const (typed, read-only variable). |
| Literal | 字面量 | A fixed value written directly in the source code, such as 42 (integer literal), 3.14 (floating-point literal), 'A' (character literal), or "hello" (string literal). |
| Identifier | 标识符 | The name given to a variable, function, or other user-defined entity. Must begin with a letter or underscore, followed by letters, digits, or underscores. Case-sensitive. |
| Keyword (reserved word) | 关键字(保留字) | A word with a predefined meaning in C (e.g., int, return, while) that cannot be used as an identifier. |
| Data type | 数据类型 | A classification that specifies the kind of value a variable can hold and the amount of memory allocated for it (e.g., int, float, double, char). |
| Type modifier | 类型修饰符 | A qualifier that alters the size or sign of a base type: signed, unsigned, short, long. |
| Declaration | 声明 | The act of informing the compiler of a variable's name and type, e.g., int age;. Memory is reserved but the value is indeterminate. |
| Assignment | 赋值 | The act of storing a value in a previously declared variable, e.g., age = 25;. |
| Initialisation | 初始化 | Declaring and assigning a value in a single statement, e.g., int age = 25;. |
| Operator precedence | 运算符优先级 | The hierarchy that determines the order in which operators are evaluated within an expression. |
| Associativity | 结合性 | The direction (left-to-right or right-to-left) in which operators of equal precedence are evaluated. |
| Implicit conversion | 隐式类型转换 | An automatic type conversion performed by the compiler when operands of different types appear in the same expression (e.g., int promoted to float). |
| Explicit casting | 显式类型转换(强制类型转换) | A programmer-directed type conversion using the cast operator, e.g., (int)3.14. |
| ASCII | ASCII 字符编码 | American Standard Code for Information Interchange — a character encoding in which each character is represented by a numeric value (e.g., 'A' = 65). |
sizeof | sizeof 运算符 | A compile-time operator that returns the size, in bytes, of a type or variable. |
4. Core Concepts
4.1 Variables and Constants
A variable is a named region of memory that stores a value of a particular type. You may think of it as a labelled box: the label is the variable's name, the size of the box is determined by its type, and the contents of the box are its current value. During programme execution the contents may change — hence the term "variable".
A constant, by contrast, is a value that is fixed once established and cannot be modified thereafter. Constants serve two important purposes: they make code more readable (the name PI is more meaningful than the bare number 3.14159) and they prevent accidental modification of values that should remain unchanged.
Consider the following declarations:
int age = 25; /* variable — value may change later */
const float pi = 3.14f; /* constant — value is fixed */
The compiler enforces the immutability of pi; any attempt to write pi = 3.0; after its declaration will produce a compilation error.
Minimal example
File: p01_var_const.c
Build: gcc p01_var_const.c -o p01_var_const
Run: ./p01_var_const
#include <stdio.h>
int main() {
int age = 25;
const float pi = 3.14159f;
printf("Age: %d\n", age);
printf("Pi: %.5f\n", pi);
age = 26; /* permitted — age is a variable */
printf("Age next year: %d\n", age);
/* pi = 3.0; — would cause a compilation error */
return 0;
}
What you should see: The programme prints the initial age, the value of pi, and the updated age.
Takeaway: Variables hold mutable data; constants hold immutable data. Prefer constants for values that should never change.
4.2 Literal Constants
A literal constant is a fixed value that appears directly in the source code. Unlike a variable, a literal has no name and no memory address that you can refer to; it is simply a value embedded in an expression or assignment.
C recognises four principal categories of literals:
- Integer literals: whole numbers written in decimal (e.g.,
42), octal (e.g.,052, prefixed with0), or hexadecimal (e.g.,0x2A, prefixed with0x). - Floating-point literals: decimal numbers with a fractional part (e.g.,
3.14) or in scientific notation (e.g.,3.14e2for 314.0). By default these are of typedouble; appendingf(e.g.,3.14f) makes themfloat. - Character literals: a single character enclosed in single quotes (e.g.,
'A'). Internally stored as an integer equal to the character's ASCII code. - String literals: a sequence of characters enclosed in double quotes (e.g.,
"Hello, World!"). These are stored as arrays ofcharterminated by a null character'\0'(covered in detail in Lecture 4).
Takeaway: Literals are the raw values from which expressions are built; understanding their types helps avoid subtle bugs in arithmetic and assignment.
4.3 Naming Identifiers
Every variable, function, and user-defined type in C is referred to by an identifier — a name chosen by the programmer. The C language imposes strict rules on what constitutes a valid identifier and, beyond those rules, the programming community observes conventions that promote clarity and consistency.
Rules (enforced by the compiler):
- An identifier must begin with a letter (
A–Z,a–z) or an underscore (_). - Subsequent characters may be letters, digits (
0–9), or underscores. - No spaces, hyphens, or special characters (
$,%,*,#, etc.) are permitted. - Identifiers are case-sensitive:
age,Age, andAGEare three distinct names. - Reserved keywords (
int,float,return,while, etc.) cannot be used as identifiers. - Since C99, the compiler guarantees significance of at least 63 characters for internal identifiers and 31 for external ones.
Conventions (community practice):
- Begin variable names with a lowercase letter.
- Use meaningful, descriptive names:
surface_arearather thansa. - Separate words with underscores (
surface_area) or use camelCase (surfaceArea); be consistent within a project. - Use ALL_UPPERCASE for symbolic constants defined with
#define(e.g.,#define MAX_AGE 200).
Quick self-test — which of the following are legal identifiers?
| Candidate | Legal? | Reason |
|---|---|---|
AREA | Yes | Begins with a letter; all uppercase is valid. |
3D | No | Begins with a digit. |
Last-Chance | No | Contains a hyphen. |
x_yt3 | Yes | Letters, underscore, digit — all permitted. |
num$ | No | Contains the special character $. |
lucky** | No | Contains the special character *. |
area_under_the_curve | Yes | Long but perfectly valid. |
num42 | Yes | Digit is not at the start. |
#values | No | Begins with #. |
pi | Yes | Short, lowercase, meaningful. |
%done | No | Begins with %. |
Takeaway: Choose names that are valid, meaningful, and consistent with established conventions; your future self (and your collaborators) will thank you.
4.4 Data Types and Their Sizes
Every variable in C has a data type that determines two things: (a) the set of values the variable can represent, and (b) the amount of memory allocated for it. The four fundamental types are:
| Type | Purpose | Typical size | Typical range |
|---|---|---|---|
char | A single character (or small integer) | 1 byte | −128 to 127 (signed) or 0 to 255 (unsigned) |
int | An integer | 4 bytes | −2,147,483,648 to 2,147,483,647 |
float | A single-precision floating-point number | 4 bytes | ≈ ±3.4 × 10³⁸ (6–7 significant digits) |
double | A double-precision floating-point number | 8 bytes | ≈ ±1.7 × 10³⁰⁸ (15–16 significant digits) |
These sizes are typical on modern 64-bit systems but are not guaranteed by the C standard; they may vary by platform. To determine the actual size on your system, use the sizeof operator.
The header <limits.h> defines macros for the minimum and maximum values of integer types (e.g., INT_MIN, INT_MAX, CHAR_MIN, CHAR_MAX, UINT_MAX), and <float.h> provides analogous macros for floating-point types (e.g., FLT_MIN, FLT_MAX, FLT_DIG).
Minimal example
File: p01_sizes.c
Build: gcc p01_sizes.c -o p01_sizes
Run: ./p01_sizes
#include <stdio.h>
#include <limits.h>
#include <float.h>
int main() {
printf("int: %zu bytes, range [%d, %d]\n",
sizeof(int), INT_MIN, INT_MAX);
printf("unsigned int: %zu bytes, range [0, %u]\n",
sizeof(unsigned int), UINT_MAX);
printf("char: %zu byte, range [%d, %d]\n",
sizeof(char), CHAR_MIN, CHAR_MAX);
printf("float: %zu bytes, precision %d digits\n",
sizeof(float), FLT_DIG);
printf("double: %zu bytes, precision %d digits\n",
sizeof(double), DBL_DIG);
return 0;
}
What you should see: The sizes and ranges of each type on your system, confirming the typical values listed above.
Takeaway: Always choose the narrowest type that can represent your data; use sizeof and the macros in <limits.h> / <float.h> to verify assumptions.
4.5 The ASCII Character Encoding
C does not have a dedicated "character" type in the way that some higher-level languages do. Instead, char is simply a small integer type, and characters are represented by their numeric codes as defined by the ASCII standard. This means that character values can participate in arithmetic: for example, 'A' + 1 evaluates to 66, which is the ASCII code for 'B'.
Some particularly useful ASCII relationships to remember:
- Uppercase letters
'A'through'Z'occupy codes 65–90. - Lowercase letters
'a'through'z'occupy codes 97–122. - The digit characters
'0'through'9'occupy codes 48–57. - The difference between a lowercase letter and its uppercase counterpart is always 32 (e.g.,
'a' - 'A' == 32).
Minimal example
File: p01_ascii.c
Build: gcc p01_ascii.c -o p01_ascii
Run: ./p01_ascii
#include <stdio.h>
int main() {
int ch = 'B'; /* ASCII value 66 */
int next_ch = ch + 1; /* 67 = 'C' */
int prev_ch = ch - 1; /* 65 = 'A' */
printf("ch = %c (ASCII %d)\n", ch, ch);
printf("next_ch = %c (ASCII %d)\n", next_ch, next_ch);
printf("prev_ch = %c (ASCII %d)\n", prev_ch, prev_ch);
return 0;
}
What you should see: ch = B (ASCII 66), next_ch = C (ASCII 67), prev_ch = A (ASCII 65).
Takeaway: Characters in C are integers; arithmetic on character values is both legal and frequently useful.
4.6 Type Modifiers: signed, unsigned, short, long
The four base types can be qualified with type modifiers that alter either the range of representable values or the amount of memory allocated:
signed(default forintandchar): the variable can hold both negative and positive values. One bit is used for the sign, reducing the positive range by half.unsigned: the variable holds only non-negative values (zero and positive). The full bit-width is available for magnitude, effectively doubling the positive range compared with the signed variant.short: requests a smaller integer type, typically 2 bytes. Useful when memory is limited and the value range is known to be small.long: requests a larger integer type, typically 8 bytes on 64-bit systems. Necessary when values exceed the range ofint.
These modifiers may be combined: unsigned long int is a valid (and common) type. When the base type is int, it may be omitted for brevity: unsigned long is equivalent to unsigned long int.
Takeaway: Use modifiers deliberately — unsigned when negative values are impossible, long when the range of int is insufficient — but always verify sizes with sizeof if portability matters.
4.7 Declaration, Assignment, and Initialisation
Working with variables in C involves up to three distinct operations, which beginners often conflate.
Declaration reserves memory for a variable of a specified type and associates it with a name. The syntax is:
<data_type> <variable_name>;
For example, int age; instructs the compiler to allocate space for an integer and label it age. Crucially, if you do not provide an initial value, the contents of that memory are indeterminate — they contain whatever data happened to be there previously. Reading an uninitialised variable is undefined behaviour and a frequent source of bugs.
Assignment stores a value in a previously declared variable:
<variable_name> = <expression>;
The = operator evaluates the expression on the right-hand side and places the result into the variable on the left-hand side. Only a single variable (an lvalue) may appear on the left.
Initialisation combines declaration and assignment in a single statement:
<data_type> <variable_name> = <value>;
For example, int age = 25; both declares age and sets it to 25. Initialisation at the point of declaration is strongly recommended, as it eliminates the risk of using an uninitialised variable.
Minimal example
File: p01_decl.c
Build: gcc p01_decl.c -o p01_decl
Run: ./p01_decl
#include <stdio.h>
int main() {
/* Declaration */
int my_age;
float salary;
char gender;
/* Assignment */
my_age = 25;
salary = 10000.50f;
gender = 'M';
/* Output */
printf("My Age: %d\n", my_age);
printf("Salary: %.2f\n", salary);
printf("Gender: %c\n", gender);
return 0;
}
What you should see: My Age: 25, Salary: 10000.50, Gender: M.
Takeaway: Declare variables with meaningful names, initialise them immediately where possible, and never read a variable before it has been assigned a definite value.
4.8 Constants: #define versus const
C provides two mechanisms for defining constants, each with distinct characteristics.
#define is a preprocessor directive. Before compilation, the preprocessor replaces every occurrence of the macro name with the specified value — a simple textual substitution. #define constants have no type information, occupy no memory, and are conventionally written in ALL_UPPERCASE.
#define PI 3.14159
#define MAX_AGE 200
const declares a typed, read-only variable. It is processed by the compiler (not the preprocessor), which means the compiler can perform type checking and the debugger can inspect its value. A const variable occupies memory and obeys normal scoping rules.
const int days_in_week = 7;
const float pi = 3.14159f;
The following table summarises the principal differences:
| Property | #define | const |
|---|---|---|
| Processing phase | Preprocessor | Compiler |
| Type checking | None | Full |
| Memory allocation | None (textual substitution) | Yes |
| Scope | Global (from point of definition) | Block or function |
| Debugging | Harder (name not visible to debugger) | Easier |
| Modifiability | Can be #undef'd and redefined | Cannot be modified |
In modern C, const is generally preferred for most constants because of its type safety and debuggability. #define remains appropriate for conditional compilation macros, include guards, and cases where a compile-time constant expression is required in older C standards.
Takeaway: Use const for typed constants within functions; reserve #define for macros, include guards, and platform-level configuration.
4.9 Arithmetic Operators
C provides five arithmetic operators for numeric computation:
| Operator | Operation | Example | Result |
|---|---|---|---|
+ | Addition | 9 + 2 | 11 |
- | Subtraction | 9 - 2 | 7 |
* | Multiplication | 9 * 2 | 18 |
/ | Division | 9 / 2 | 4 (integer division) |
% | Modulus (remainder) | 9 % 2 | 1 |
Two points deserve special attention:
-
Integer division truncates towards zero. When both operands are integers, the
/operator discards any fractional part. Thus9 / 2yields4, not4.5. If you need the true quotient, at least one operand must be a floating-point type:9.0 / 2yields4.5. -
The modulus operator
%is defined only for integer operands. Attempting2.5 % 1.2will produce a compilation error. The result ofa % bhas the same sign asa(in C99 and later).
Minimal example
File: p01_arith.c
Build: gcc p01_arith.c -o p01_arith
Run: ./p01_arith
#include <stdio.h>
int main() {
int a = 9, b = 2;
printf("%d + %d = %d\n", a, b, a + b);
printf("%d - %d = %d\n", a, b, a - b);
printf("%d * %d = %d\n", a, b, a * b);
printf("%d / %d = %d\n", a, b, a / b); /* truncated */
printf("%d %% %d = %d\n", a, b, a % b);
return 0;
}
What you should see: 9 + 2 = 11, 9 - 2 = 7, 9 * 2 = 18, 9 / 2 = 4, 9 % 2 = 1.
Takeaway: Integer division truncates; use floating-point operands when a fractional result is required. The % operator works only with integers.
4.10 Operator Precedence and Associativity
When an expression contains multiple operators, the compiler must determine the order in which they are evaluated. This order is governed by two rules: precedence and associativity.
The following table lists the arithmetic operators in descending order of precedence (highest first):
| Precedence | Operators | Associativity |
|---|---|---|
| 1 (highest) | () (parentheses) | Left-to-right |
| 2 | Unary +, unary - | Right-to-left |
| 3 | *, /, % | Left-to-right |
| 4 (lowest) | Binary +, binary - | Left-to-right |
When two operators have the same precedence, associativity determines the evaluation direction. For most arithmetic operators, this is left-to-right: 10 - 3 - 2 is evaluated as (10 - 3) - 2 = 5, not 10 - (3 - 2) = 9.
Consider the expression 3 + 5 * 2. Because * has higher precedence than +, the multiplication is performed first: 3 + (5 * 2) = 3 + 10 = 13. If addition should be performed first, use parentheses: (3 + 5) * 2 = 16.
As a practical guideline: when in doubt, add parentheses. They cost nothing at runtime and make your intent explicit.
Practice evaluation (work through these by hand):
8 + 4 / 2→8 + 2→1015 % 4 * 2→3 * 2→6(10 - 3) * 2 + 1→7 * 2 + 1→14 + 1→15-(3 + 5) * 2→-(8) * 2→-8 * 2→-16
Takeaway: Memorise that multiplication, division, and modulus bind more tightly than addition and subtraction; use parentheses liberally to clarify complex expressions.
4.11 Type Conversions and Casting
C is a statically typed language, yet it permits expressions that mix operands of different types. When this occurs, the compiler must reconcile the types, and it does so via type conversion. There are two forms.
Implicit conversion (promotion) occurs automatically when the compiler widens a narrower type to a wider one to preserve information. The general promotion hierarchy is:
char → int → long → float → double → long double
For example, in the expression 5 + 2.0, the integer 5 is implicitly promoted to double before the addition is performed, and the result is 7.0 (a double).
When a wider type is assigned to a narrower variable, the compiler performs an implicit narrowing conversion, which may lose information. For instance:
float x = 2.5f, y = 1.2f;
int result = x + y; /* 3.7 is truncated to 3 */
The compiler may issue a warning for such conversions, and rightly so.
Explicit casting is a deliberate, programmer-directed conversion using the cast operator:
float pi = 3.14159f;
int pi_int = (int)pi; /* pi_int is 3; fractional part discarded */
Casting is necessary when you want to perform integer division on two floating-point values, or when passing values to functions that expect a specific type. It also serves as documentation of intent: it signals to the reader that the potential loss of precision is acknowledged and deliberate.
A cautionary note on overflow: casting to a narrower type can produce unexpected results if the value exceeds the target type's range. For example:
int large = 2147483647; /* INT_MAX */
short small = (short)large; /* undefined/wrapped value */
On a typical system where short is 2 bytes, the value wraps around and the result is -1, which is almost certainly not what was intended.
Minimal example
File: p01_cast.c
Build: gcc p01_cast.c -o p01_cast
Run: ./p01_cast
#include <stdio.h>
int main() {
/* Implicit conversion: int to float */
int age = 25;
float age_float = age;
printf("Implicit: %d -> %f\n", age, age_float);
/* Explicit casting: float to int */
float pi = 3.14159f;
int pi_int = (int)pi;
printf("Explicit: %f -> %d\n", pi, pi_int);
/* Mixed-type expression */
printf("5 / 2 = %d\n", 5 / 2); /* integer division */
printf("5.0 / 2 = %f\n", 5.0 / 2); /* float division */
return 0;
}
What you should see: Implicit: 25 -> 25.000000, Explicit: 3.141590 -> 3, 5 / 2 = 2, 5.0 / 2 = 2.500000.
Takeaway: Implicit conversions widen safely; narrowing conversions (whether implicit or explicit) may lose data. Use explicit casts to document deliberate conversions and heed compiler warnings.
5. Worked Examples
Example 1: Declaring, Assigning, and Printing Variables
Problem: Write a programme that declares an integer variable age, assigns it the value 30, and prints the result.
Steps:
- Declare
int age;. - Assign
age = 30;. - Print using
printfwith%d.
Reference code
File: p01_ex1.c
Build: gcc p01_ex1.c -o p01_ex1
Run: ./p01_ex1
#include <stdio.h>
int main() {
int age;
age = 30;
printf("Age: %d\n", age);
return 0;
}
Output:
Age: 30
Why it works: int age; reserves 4 bytes for an integer. The assignment age = 30; writes the value 30 into that memory. The %d format specifier in printf reads the integer value and formats it as a decimal string.
Example 2: Computing the Area of a Circle with Constants
Problem: Define the constant π using #define and the radius using const. Compute and display the area of a circle.
Steps:
- Define
PIas a preprocessor macro. - Declare
radiusas aconst int. - Compute
area = PI * radius * radius. - Print with three decimal places using
%.3f.
Reference code
File: p01_ex2.c
Build: gcc p01_ex2.c -o p01_ex2
Run: ./p01_ex2
#include <stdio.h>
#define PI 3.14159
int main() {
const int radius = 7;
float area = PI * radius * radius;
printf("Area of the circle: %.3f\n", area);
return 0;
}
Output:
Area of the circle: 153.938
Why it works: The preprocessor replaces PI with 3.14159 before compilation. The expression PI * radius * radius involves a double literal multiplied by int values; implicit promotion converts the integers to double for the multiplication. The result is then narrowed to float when stored in area. The %.3f specifier formats the output to three decimal places.
Example 3: Swapping Two Variables
Problem: Given two integer variables a and b, swap their values using a temporary variable and print the result before and after the swap.
Steps:
- Declare and initialise
a = 5andb = 10. - Print the values before swapping.
- Introduce a temporary variable
temp, and perform the three-step swap:temp = a; a = b; b = temp;. - Print the values after swapping.
Reference code
File: p01_ex3.c
Build: gcc p01_ex3.c -o p01_ex3
Run: ./p01_ex3
#include <stdio.h>
int main() {
int a = 5, b = 10;
printf("Before swap:\n");
printf("a = %d, b = %d\n", a, b);
int temp = a;
a = b;
b = temp;
printf("After swap:\n");
printf("a = %d, b = %d\n", a, b);
return 0;
}
Output:
Before swap:
a = 5, b = 10
After swap:
a = 10, b = 5
Why it works: Without the temporary variable, the first assignment (a = b) would overwrite the original value of a, making it irrecoverable. The temporary variable preserves a's original value so that it can be assigned to b in the third step. This is a fundamental algorithmic pattern that recurs frequently in sorting and data manipulation.
6. Common Mistakes
-
Using an uninitialised variable: After
int x;, the value ofxis indeterminate. Reading it before assignment is undefined behaviour and may produce different results on each run. Always initialise variables at the point of declaration when possible. -
Confusing
=(assignment) with==(equality test): Writingif (x = 5)assigns5toxand always evaluates as true, rather than testing whetherxequals5. This error will become especially relevant in Lecture 2 (conditions), but the habit of understanding=as assignment begins here. -
Integer division surprise:
5 / 2yields2, not2.5. Beginners frequently expect a fractional result. If a floating-point quotient is needed, ensure at least one operand is a floating-point type:5.0 / 2or(float)5 / 2. -
Applying
%to floating-point operands: The modulus operator is defined only for integers. Writing2.5 % 1.2causes a compilation error. Use thefmodfunction from<math.h>if a floating-point remainder is required. -
Ignoring compiler warnings about narrowing conversions: When assigning a
floatresult to anintvariable, data is silently truncated. Compile with-Wall(all warnings) to catch such issues:gcc -Wall -o prog prog.c. Treat every warning as an error until you understand and have deliberately accepted the conversion.
7. Practice
7.1 Basic
-
P1: Declare an integer variable
age, assign it the value30, and print it. Acceptance: Output is exactlyAge: 30. -
P2: Declare and initialise a
floatvariableheightwith the value169.5. Print it asHeight: 169.5 cm(use%.1f). Acceptance: Output matches the format above.
7.2 Required
-
P3: Define
PIusing#define(value3.14159) andradiususingconst int(value7). Compute and print the area of a circle to three decimal places. Acceptance: Output isArea of the circle: 153.938. -
P4: Declare two integers
num1 = 10andnum2 = 3. Print the quotient and remainder. Input/Output: OutputQuotient: 3andRemainder: 1. Acceptance: Both values correct. -
P5: Declare three variables with meaningful names (
age,height,is_student) following naming conventions, assign values, and print them. Acceptance: Output shows all three values with descriptive labels. -
P6: Declare variables of types
int,float,double, andchar. Print the size of each usingsizeof. Acceptance: Output matches the sizes on your system (typically 4, 4, 8, 1 bytes). -
P7: Demonstrate implicit type conversion by assigning an
intvalue to afloatvariable and printing the result. Acceptance: Output isFloat Value: 10.000000. -
P8: Demonstrate explicit type casting by casting
169.5(afloat) tointand printing the result. Acceptance: Output isInteger Height: 169. -
P9: Define a
const int max_speed = 120;and print its value. Then uncomment a line that attempts to modifymax_speedand observe the compiler error. Include the error message as a comment in your submitted code. Acceptance: Programme compiles and prints120; comment documents the error. -
P10: Swap two integer variables using a temporary variable and print the values before and after the swap. Acceptance: Output matches the format in Example 3.
7.3 Optional
-
P11: Write a programme that evaluates and prints the result of each of the following expressions, confirming your hand calculations:
8 + 4 / 2,15 % 4 * 2,(10 - 3) * 2 + 1,-(3 + 5) * 2,5.0 / 2 + 3,(int)7.8 / 2,'A' + 2. For each, print the expression as a string alongside the computed result. -
P12: Write a programme that reads two integers from the user, performs all five arithmetic operations, and prints the results in a neatly formatted table. Handle the case where the second integer is zero (print an error message instead of performing division and modulus).
-
P13: Write a programme that reads a character from the user and prints its ASCII code, the next character in the ASCII table, and the previous character. For example, entering
'B'should displayB (66),C (67),A (65).
8. Mini Task
Goal: Create a simple unit converter that demonstrates variables, constants, arithmetic operators, and type conversions.
Deliverables:
README.md: Compilation and execution instructions with sample terminal sessions.src/p01_converter.c: A programme that:- Defines conversion constants using
#defineorconst(e.g.,#define KM_PER_MILE 1.60934). - Prompts the user to enter a distance in miles (as a
float). - Converts the distance to kilometres and prints the result to two decimal places.
- Also prints the truncated integer part of the result (using explicit casting) and the fractional remainder.
- Defines conversion constants using
src/p01_swap.c: The variable-swap programme from P10.report.md: A brief reflection (5–10 sentences) on any difficulties encountered, particularly regarding type conversions or unexpected arithmetic results.
9. Further Reading
- Kernighan, B. W. & Ritchie, D. M. (1988). The C Programming Language (2nd ed.). Prentice Hall — Chapter 2: "Types, Operators and Expressions".
- King, K. N. (2008). C Programming: A Modern Approach (2nd ed.). W. W. Norton — Chapters 4 ("Expressions") and 7 ("Basic Types").
- ISO/IEC 9899:2018 (C17 Standard) — §6.2.5 "Types", §6.3 "Conversions", §6.5 "Expressions". Available via national standards bodies or as a working draft at https://www.open-std.org/jtc1/sc22/wg14/.
10. Appendix
A. C Reserved Keywords (Complete List)
The following identifiers are reserved and may not be used as variable or function names. Keywords marked with a standard version were introduced in that revision of the language.
auto, break, case, char, const, continue, default, do, double, else, enum, extern, float, for, goto, if, inline (C99), int, long, register, restrict (C99), return, short, signed, sizeof, static, struct, switch, typedef, union, unsigned, void, volatile, while.
C23 additions: alignas, alignof, bool, constexpr, false, nullptr, static_assert, thread_local, true, typeof, typeof_unqual.
B. Partial ASCII Reference
| Decimal | Character | Decimal | Character | Decimal | Character |
|---|---|---|---|---|---|
| 48–57 | '0'–'9' | 65–90 | 'A'–'Z' | 97–122 | 'a'–'z' |
| 32 | Space | 10 | Newline (\n) | 0 | Null (\0) |
A complete ASCII table is available in the lecture slides and in any C reference manual.
C. Useful Compiler Flags
When compiling the exercises in this chapter, consider using the following GCC flags:
gcc -Wall -Wextra -std=c17 -o prog prog.c
-Wall: Enable all common warnings.-Wextra: Enable additional warnings beyond-Wall.-std=c17: Compile according to the C17 standard.
These flags help catch uninitialised variables, implicit narrowing conversions, unused variables, and other issues that are easy to overlook.