Unit 2: Pointers, Reference Variables, Arrays and String Concepts - Subjective Questions
CSE202 — Object Oriented Programming • Practice Questions with Detailed Answers
20 questions
Define a void pointer in C++. Explain its characteristics, limitations, and type casting with a suitable example.
A void pointer, declared as void*, is a generic pointer that can store the address of an object of any data type.
Characteristics:
- It can point to an
int,float, object, or any other data type. - It provides flexibility when the pointed-to type is not known in advance.
- It stores only an address and does not retain type information.
Limitations:
- A void pointer cannot be dereferenced directly.
- Pointer arithmetic cannot be performed on it in standard C++ because the size of the pointed-to type is unknown.
- It must be converted to an appropriate typed pointer before dereferencing.
Example:
int value = 25;
void* ptr = &value;
int* intPtr = static_cast<int*>(ptr);
cout << *intPtr; // 25Here, ptr stores the address of value. It is converted back to int* before dereferencing. An incorrect conversion can produce undefined behavior.
Explain pointer arithmetic in C++. Describe the operations permitted on pointers and illustrate how the data type affects address movement.
Pointer arithmetic allows a pointer to move between elements of the same array.
Permitted operations include:
- Increment:
ptr++ - Decrement:
ptr-- - Addition of an integer:
ptr + n - Subtraction of an integer:
ptr - n - Subtraction of two pointers belonging to the same array
- Relational comparison of pointers within the same array
If ptr points to an element of type T, then ptr + n advances by n * sizeof(T) bytes.
int values[] = {10, 20, 30, 40};
int* ptr = values;
cout << *ptr; // 10
cout << *(ptr + 2); // 30
ptr++;
cout << *ptr; // 20For an int occupying 4 bytes, incrementing an int* typically increases its stored address by 4 bytes. Arithmetic is valid only within an array or one position past its end; dereferencing an out-of-range pointer causes undefined behavior.
What is a pointer to pointer? Explain its declaration, initialization, and dereferencing. Mention two practical applications.
A pointer to pointer stores the address of another pointer. It introduces an additional level of indirection.
Declaration and use:
int value = 50;
int* ptr = &value;
int** ptrToPtr = &ptr;
cout << *ptr; // 50
cout << **ptrToPtr; // 50ptrpoints tovalue.ptrToPtrpoints toptr.*ptrToPtrgivesptr.**ptrToPtrgivesvalue.
Applications:
- Allowing a function to modify a caller's pointer.
- Representing dynamically allocated two-dimensional arrays.
- Implementing data structures such as linked lists and trees.
- Managing arrays of C-style strings, such as command-line arguments.
Each * represents one level of indirection, so the number and types of indirections must match the declaration.
Define dangling pointer, wild pointer, and null pointer. Compare their causes and explain how each should be handled safely.
Dangling pointer: A pointer that refers to memory that is no longer valid.
int* ptr = new int(10);
delete ptr;
ptr = nullptr;After delete, the pointer would be dangling until it is assigned nullptr. A pointer can also dangle when it refers to a local variable that has gone out of scope.
Wild pointer: An uninitialized pointer containing an indeterminate address.
int* ptr; // Wild pointerDereferencing it causes undefined behavior. Initialize pointers immediately.
int* ptr = nullptr;Null pointer: A pointer deliberately assigned no valid object address.
int* ptr = nullptr;A null pointer can be tested safely, but it must not be dereferenced.
Safe practices:
- Initialize pointers with a valid address or
nullptr. - Set an owning raw pointer to
nullptrafterdelete. - Avoid returning addresses of local variables.
- Check for
nullptrbefore dereferencing when null is a valid state. - Prefer automatic storage and smart pointers for ownership.
Explain null pointer assignment in modern C++. Compare nullptr, NULL, and 0, and demonstrate a safe null check.
Null pointer assignment gives a pointer a special value indicating that it does not point to any object or function.
int* ptr = nullptr;Comparison:
nullptris the modern C++ null pointer literal introduced in C++11. Its type isstd::nullptr_t, and it converts safely to any pointer type.NULLis usually a macro that may expand to0or another implementation-defined null constant.0is an integer literal that can also act as a null pointer constant in pointer contexts.
nullptr is preferred because it avoids ambiguity in overloaded functions.
void process(int);
void process(int*);
process(nullptr); // Selects process(int*)Safe check:
if (ptr != nullptr) {
cout << *ptr;
}Assigning nullptr does not allocate memory and does not automatically release previously allocated memory. If ptr owns dynamic memory, that memory must be released before overwriting the pointer.
Describe the problems involved when a class contains a pointer data member. Explain deep copying using the Rule of Three with a suitable class design.
A class containing an owning pointer must manage the lifetime of dynamically allocated memory. The compiler-generated copy operations perform a shallow copy, meaning two objects receive the same address. This can cause shared unintended modification, dangling pointers, and double deletion.
The Rule of Three states that a class requiring any one of the following usually requires all three:
- Destructor
- Copy constructor
- Copy assignment operator
class Number {
int* value;
public:
Number(int v) : value(new int(v)) {}
~Number() {
delete value;
}
Number(const Number& other)
: value(new int(*other.value)) {}
Number& operator=(const Number& other) {
if (this != &other) {
int* temp = new int(*other.value);
delete value;
value = temp;
}
return *this;
}
int get() const {
return *value;
}
};Each copied object owns a separate allocation, producing a deep copy. In modern C++, std::unique_ptr, std::vector, or another resource-managing type is usually preferable because it reduces manual memory-management errors.
What is a pointer to an object? Explain how object members are accessed through object pointers and how dynamic objects are created and destroyed.
A pointer to an object stores the address of an instance of a class.
class Student {
public:
string name;
void display() const {
cout << name;
}
};
Student s;
Student* ptr = &s;
ptr->name = "Asha";
ptr->display();The arrow operator -> accesses members through a pointer. The expression ptr->name is equivalent to (*ptr).name.
An object may also be created dynamically:
Student* studentPtr = new Student;
studentPtr->name = "Ravi";
studentPtr->display();
delete studentPtr;
studentPtr = nullptr;new constructs the object and returns its address. delete invokes the object's destructor and releases the allocated memory. In modern C++, a smart pointer such as std::unique_ptr<Student> is generally preferred for an owned dynamic object.
Explain the purpose and behavior of the this pointer in C++. Discuss its use in resolving name conflicts, returning the current object, and preventing self-assignment.
The this pointer is an implicit pointer available inside every non-static member function. It points to the object on which the function was invoked.
Resolving name conflicts:
class Item {
int value;
public:
void setValue(int value) {
this->value = value;
}
};Here, this->value refers to the data member, while value refers to the parameter.
Returning the current object:
Item& setValue(int value) {
this->value = value;
return *this;
}Returning *this by reference supports chained calls.
Preventing self-assignment:
if (this != &other) {
// Perform assignment
}Important points:
thisis not available in static member functions because they are not associated with a particular object.- In a non-const member function, its conceptual type is
ClassName* const. - In a const member function, its conceptual type is
const ClassName* const.
Define an array of objects. Explain how constructors are invoked for its elements and demonstrate static and dynamic arrays of objects.
An array of objects is a sequence of class instances of the same type stored contiguously.
class Point {
int x;
public:
Point() : x(0) {}
Point(int value) : x(value) {}
int getX() const { return x; }
};
Point points[3] = {Point(10), Point(20), Point(30)};
cout << points[1].getX(); // 20Constructors are called in increasing index order. When the array goes out of scope, destructors are called in reverse order.
A dynamic array can be created as follows:
Point* points = new Point[3];
cout << points[0].getX();
delete[] points;
points = nullptr;For new Point[3], an accessible default constructor is normally required. The memory must be released using delete[], not scalar delete. In modern C++, std::vector<Point> is usually safer and supports convenient initialization and resizing.
Describe different ways of defining, initializing, and assigning std::string objects in C++. How do they differ from C-style strings?
std::string is the standard C++ class for managing sequences of characters. It is declared in the <string> header.
Definition and initialization:
string s1; // Empty string
string s2 = "Object Oriented"; // From a string literal
string s3("Programming"); // Direct initialization
string s4(5, 'A'); // "AAAAA"
string s5 = s2; // Copy initializationAssignment:
s1 = "C++";
s3 = s1;
s4.assign("Pointers");Difference from C-style strings:
std::stringmanages its own memory automatically.- Its size can change dynamically.
- It supports assignment, concatenation, comparison, and searching through operators and member functions.
- A C-style string is a null-terminated character array and often requires functions such as
strcpyandstrlen. std::string::c_str()provides a null-terminated character representation when interaction with a C API is required.
Explain any five commonly used std::string member functions with syntax and examples.
Common std::string member functions include:
size()orlength(): Returns the number of characters.
text.size()at(index): Returns the character at an index with bounds checking.
text.at(2)substr(pos, count): Returns a substring.
text.substr(0, 3)find(value): Returns the position of the first match orstring::npos.
text.find("gram")compare(other): Performs lexicographical comparison.
text.compare(other)empty(): Tests whether the string contains no characters.
text.empty()
Example:
string text = "Programming";
cout << text.length(); // 11
cout << text.at(0); // P
cout << text.substr(3, 4); // gram
if (text.find("gram") != string::npos) {
cout << "Found";
}Unlike operator[], at() throws std::out_of_range when the index is invalid, making it useful when checked access is required.
What are string class modifiers? Explain the operation of append, insert, erase, replace, push_back, pop_back, and clear.
String modifiers are member functions that change the contents or size of a std::string object.
string text = "Object";append()adds characters at the end:text.append(" Oriented").insert()adds characters at a specified position:text.insert(0, "An ").erase()removes characters:text.erase(0, 3).replace()substitutes part of the string:text.replace(0, 6, "Class").push_back()adds one character at the end:text.push_back('!').pop_back()removes the last character:text.pop_back().clear()removes all characters and makes the string empty:text.clear().
Most operations may change the string's storage. Therefore, pointers, references, and iterators referring to its characters may be invalidated. pop_back() should only be called when the string is not empty.
Distinguish between pointer variables and reference variables in C++. Include declaration, initialization, reassignment, nullability, arithmetic, and syntax of access.
| Basis | Pointer | Reference |
|---|---|---|
| Declaration | int* ptr; |
int& ref = value; |
| Initialization | May be initialized later | Must normally be initialized when declared |
| Null state | Can hold nullptr |
Must refer to a valid object under normal use |
| Reassignment | Can be changed to point to another object | Cannot be reseated after initialization |
| Access | Requires *ptr to access the object |
Used like the original variable |
| Arithmetic | Supports restricted pointer arithmetic | Reference arithmetic does not exist |
| Address | Stores an address as its value | Acts as an alias for an existing object |
int a = 10;
int b = 20;
int* ptr = &a;
ptr = &b; // Pointer now points to b
int& ref = a;
ref = b; // Assigns b's value to a; ref still refers to aPointers are suitable for optional relationships, dynamic structures, and array traversal. References are suitable for aliases and function parameters where an object is required and pointer-like syntax is unnecessary.
Explain the declaration, initialization, and processing of a two-dimensional array inside main(). Show how its elements are stored and accessed.
A two-dimensional array is an array whose elements are themselves arrays.
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};The declaration specifies 2 rows and 3 columns. An element is accessed as matrix[row][column].
Processing with nested loops:
for (int row = 0; row < 2; ++row) {
for (int column = 0; column < 3; ++column) {
cout << matrix[row][column] << ' ';
}
cout << '\n';
}C++ stores built-in multidimensional arrays in row-major order. Thus, the physical sequence is 1, 2, 3, 4, 5, 6.
For an array declared as T array[R][C], the address calculation is conceptually:
All indexes must remain within their declared bounds because built-in arrays do not perform automatic bounds checking.
Describe how a multidimensional array can be used as a data member of a class. Write a class that accepts and displays a matrix.
A multidimensional array declared inside a class becomes part of every object of that class. Member functions can process it directly.
class Matrix {
static const int ROWS = 2;
static const int COLS = 3;
int data[ROWS][COLS]{};
public:
void read() {
for (int i = 0; i < ROWS; ++i) {
for (int j = 0; j < COLS; ++j) {
cin >> data[i][j];
}
}
}
void display() const {
for (int i = 0; i < ROWS; ++i) {
for (int j = 0; j < COLS; ++j) {
cout << data[i][j] << ' ';
}
cout << '\n';
}
}
};The empty initializer {} initializes all elements to zero. ROWS and COLS define fixed compile-time dimensions. The display() function is marked const because it does not modify the object. For dimensions determined at runtime, a container such as std::vector<std::vector<int>> or a single flat std::vector<int> can be used instead.
Explain how a built-in two-dimensional array is passed to a function. Why must all dimensions except the first normally be specified?
When a built-in two-dimensional array is passed to a function, its parameter is adjusted to a pointer to its first row.
void display(const int matrix[][3], int rows) {
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < 3; ++j) {
cout << matrix[i][j] << ' ';
}
}
}The parameter const int matrix[][3] is equivalent to:
const int (*matrix)[3]The column count must be known because the compiler needs the size of one complete row to evaluate matrix[i]. Conceptually, moving from row i to row i + 1 requires advancing by 3 * sizeof(int) bytes.
The first dimension may be omitted because it is not required for calculating an element's address. It is usually supplied separately as rows so the function knows how many rows to process. A function template taking an array by reference can preserve both dimensions at compile time.
What is a pointer to data member in C++? Explain its declaration, initialization, and use with both an object and an object pointer.
A pointer to data member identifies a non-static data member of a class without being tied to a particular object. It is different from an ordinary pointer because it needs an object to locate the actual member.
class Student {
public:
int marks;
};
int Student::*memberPtr = &Student::marks;
Student s{85};
Student* objectPtr = &s;
cout << s.*memberPtr; // 85
cout << objectPtr->*memberPtr; // 85Syntax:
- Declaration:
DataType ClassName::*pointerName - Initialization:
&ClassName::memberName - Access through an object:
object.*pointer - Access through an object pointer:
objectPointer->*pointer
A pointer to data member can be passed to reusable functions that operate on different members of the same class. It does not generally contain a simple standalone memory address because the final address depends on the object to which it is applied.
Compare a pointer to data member, an ordinary pointer, and a pointer to an object. Provide an example showing their different access operators.
These pointer forms represent different concepts:
- An ordinary pointer stores the address of a complete variable or object.
- A pointer to an object is an ordinary pointer whose pointed-to type is a class.
- A pointer to data member identifies a member within objects of a particular class.
class Box {
public:
int width;
};
int number = 5;
int* ordinaryPtr = &number;
Box box{10};
Box* objectPtr = &box;
int Box::*memberPtr = &Box::width;
cout << *ordinaryPtr; // Ordinary dereference
cout << objectPtr->width; // Member through object pointer
cout << box.*memberPtr; // Member pointer with object
cout << objectPtr->*memberPtr; // Member pointer with object pointerAn ordinary pointer can be dereferenced independently if it points to a valid object. A pointer to data member cannot be dereferenced using unary *; it must be combined with a suitable class object using .* or ->*.
Analyze the following pointer operations and explain which statements are valid or unsafe: subtracting array pointers, comparing pointers, dereferencing the one-past-end pointer, and performing arithmetic on unrelated pointers.
Consider the following array:
int values[5] = {10, 20, 30, 40, 50};
int* first = &values[1];
int* second = &values[4];Pointer subtraction:
ptrdiff_t distance = second - first; // 3This is valid because both pointers refer to elements of the same array. The result is measured in elements, not bytes.
Pointer comparison:
bool result = first < second; // trueRelational comparison has a defined ordering when both pointers refer to the same array.
One-past-end pointer:
int* end = values + 5; // Valid pointer valueThe pointer may be formed and used as a loop boundary, but *end is invalid because no array element exists there.
Unrelated pointers:
Subtracting pointers to unrelated objects has undefined behavior. Relational ordering with built-in operators is not generally meaningful for unrelated objects.
Key rule: Pointer arithmetic should remain within one array object or at most produce its one-past-end pointer.
Design a C++ class that combines an array of objects, object pointers, the this pointer, and std::string operations. Explain how these concepts interact in the design.
The following design stores an array of Student objects and uses pointers to process them:
#include <iostream>
#include <string>
using namespace std;
class Student {
string name;
public:
Student& setName(const string& name) {
this->name = name;
return *this;
}
void addSuffix(const string& suffix) {
name.append(suffix);
}
const string& getName() const {
return name;
}
};
int main() {
Student students[3];
students[0].setName("Asha");
students[1].setName("Ravi");
students[2].setName("Meera");
Student* ptr = students;
for (int i = 0; i < 3; ++i) {
(ptr + i)->addSuffix(" Kumar");
cout << (ptr + i)->getName() << '\n';
}
}Interaction of concepts:
studentsis an array of objects stored contiguously.- In pointer context,
studentspoints to its first element. (ptr + i)->getName()accesses each object using pointer arithmetic and->.this->namedistinguishes the data member from the parameter.- Returning
*thisallows member-function chaining. append()modifies eachstd::stringsafely without manual character-array management.getName()returns a const reference, avoiding an unnecessary string copy while preventing modification through the returned reference.
Define a void pointer in C++. Explain its characteristics, limitations, and type casting with a suitable example.
A void pointer, declared as void*, is a generic pointer that can store the address of an object of any data type.
Characteristics:
- It can point to an
int,float, object, or any other data type. - It provides flexibility when the pointed-to type is not known in advance.
- It stores only an address and does not retain type information.
Limitations:
- A void pointer cannot be dereferenced directly.
- Pointer arithmetic cannot be performed on it in standard C++ because the size of the pointed-to type is unknown.
- It must be converted to an appropriate typed pointer before dereferencing.
Example:
int value = 25;
void* ptr = &value;
int* intPtr = static_cast<int*>(ptr);
cout << *intPtr; // 25Here, ptr stores the address of value. It is converted back to int* before dereferencing. An incorrect conversion can produce undefined behavior.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →