Write two C++ functions that increment an integer value using two different parameter-passing techniques commonly used in embedded and firmware development:
void incrementPtr(int* x)void incrementRef(int& x)The program reads an integer from standard input, calls both functions separately, and prints the results.
You only need to implement the two functions.
The main() function is provided and must not be modified.
Assumptions:
Example 1
Input
5Output
After incrementPtr: 6
After incrementRef: 6
Example 2
Input
0Output
After incrementPtr: 1
After incrementRef: 1
A Reference in C++ is an alias (an alternative name) for an existing variable.
Unlike a pointer, which holds a memory address and can be NULL, a reference must be initialized to a valid object and cannot be changed to refer to a different object later.
Think of it as a const pointer that is automatically dereferenced for you.
1. Basic Declaration
int x = 10;
int &ref = x; // 'ref' is now an alias for 'x'
ref = 20; // Modifies 'x' directly
// x is now 202. Pass-by-Reference (Function Parameters)
In C, to modify a variable inside a function, you pass a pointer. In C++, you pass a reference.
| C Style (Pointers) | C++ Style (References) |
|---|---|
| |
Call: update(&x); | Call: update(x); |
3. const Reference (Read-Only Access)
Used to pass large objects (like structs or buffers) efficiently without copying them and without allowing modification.
struct SensorData {
float x, y, z;
uint32_t timestamp;
};
// Efficient: No copy created, but strictly Read-Only
void processData(const SensorData &data) {
// data.x = 0; // ❌ Error: Read-only
printf("%f", data.x); // ✅ OK
}| Feature | Pointer (int*) | Reference (int&) |
|---|---|---|
| Nullability | Can be NULL (needs checking). | Cannot be NULL (always valid). |
| Reassignment | Can point to different addresses. | Bound to one object forever. |
| Memory Address | Has its own address on stack. | Shares address of the target. |
| Syntax | Requires * to access value. | Accessed like a normal variable. |
| Embedded Use | Low-level hardware/buffer access. | High-level APIs and safety. |
1. Efficient Driver APIs
Passing hardware driver objects (like UART or SPI classes) by value copies the entire object, which breaks register mappings. Passing by pointer creates messy syntax (->). References offer the best of both:
// Clean syntax, no copying, no NULL checks needed
void generic_log(UART_Driver &uart, const char *msg) {
uart.send(msg);
}2. Operator Overloading
References are mandatory for operator overloading (e.g., operator=, operator[]), which allows you to treat hardware buffers like standard arrays.
3. Range-Based For Loops
When iterating over a container or array without copying elements:
// 'byte' is a reference to the actual array element
for (uint8_t &byte : rx_buffer) {
byte = 0; // Clear buffer efficiently
}4. Singleton Access
Returning a reference from a Singleton getInstance() is safer than a pointer because the user knows it will never be null.
| Pitfall | Details |
|---|---|
| ❌ Dangling Reference | Returning a reference to a local variable destroys the stack frame, leaving the reference pointing to garbage. |
| ❌ "Null" Reference | While technically impossible, dereferencing a |
| ❌ Reference to Bitfield | You cannot create a reference to a bitfield in a struct because bits don't have unique memory addresses. |
| ✅ Use References for API | Prefer const Type& for input arguments in functions to avoid unnecessary copying of structs. |
| ✅ Use Pointers for Optional | If a parameter is optional (can be NULL), use a pointer. References imply "this must exist." |