Menu
CoddyTech

C++ Pointers and References Interview Questions

Pointer arithmetic, references, const with pointers, nullptr and dangling pointers.

10 questions
  1. What is the difference between a pointer and a reference in C++?

    A reference is another name for an existing object; a pointer is a variable holding an address. A pointer can be null, re-pointed and used in arithmetic; a reference is bound once, at initialization.

    Take const T& or T& when an object must exist, and a pointer when "no object" is valid. See references vs pointers.

    Practice it: Reverse Linked List, Linked List Cycle

  2. Which can a function change: an argument by value, by reference or by pointer?

    C++
    #include <iostream>
    using namespace std;
    
    void byValue(int x) { x = 10; }
    void byRef(int& x) { x = 20; }
    void byPtr(int* x) { *x = 30; }
    
    int main() {
        int a = 1, b = 1, c = 1;
        byValue(a);
        byRef(b);
        byPtr(&c);
        cout << a << " " << b << " " << c << "\n";
    }

    It prints 1 20 30. byValue changes a copy, byRef changes b itself, and byPtr writes through the address of c.

    Pass small types by value, large read-only objects by const T&, objects you modify by T&, and a pointer when the argument may be absent.

  3. Can you make a reference refer to a different variable?

    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        int a = 1, b = 2;
        int& r = a;
        r = b;
        r = 3;
        cout << a << " " << b << "\n";
    }

    It prints 3 2. A reference cannot be re-pointed: r = b; assigns the value of b (2) to a, and r = 3; assigns 3 to a. b is never touched.

    If you need a rebindable reference, for example inside a container, use a pointer or std::reference_wrapper.

  4. What do *p, p[-1] and p - a give after p += 2?

    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        int a[] = {10, 20, 30, 40};
        int* p = a;
        p += 2;
        cout << *p << " " << p[-1] << " " << (p - a) << "\n";
    }

    It prints 30 20 2. Pointer arithmetic counts in elements, so p += 2 moves to a[2], p[-1] is a[1], and p - a is the number of elements between them.

    Moving a pointer outside the array (past one-past-the-end) is undefined behavior even without a dereference.

  5. What is the difference between const int*, int* const and const int* const?

    Read the declaration from right to left:

    • const int* p: pointer to a const int. You can move p, not change *p.
    • int* const p: const pointer. You can change *p, not p.
    • const int* const p: neither can change.
    int x = 1, y = 2;
    const int* a = &x;  a = &y;   // ok
                        // *a = 5;   error: *a is const
    int* const b = &x;  *b = 5;   // ok
                        // b = &y;   error: b is const
    
  6. What is the difference between nullptr and NULL?

    nullptr (C++11) converts to any pointer type but never to an integer. NULL is a C macro, 0 on MSVC and the integer constant __null on GCC and Clang with glibc.

    C++
    #include <iostream>
    
    void f(int) { std::cout << "f(int)\n"; }
    void f(int*) { std::cout << "f(int*)\n"; }
    
    int main() {
        f(0);
        f(nullptr);
    }

    This prints f(int) and then f(int*). f(NULL) calls f(int) on MSVC, is ambiguous with glibc, and calls f(int*) with musl (this page's runner).

  7. What is a dangling pointer, and how do you avoid it?

    A pointer or reference to an object that no longer exists; using it is undefined behavior.

    int* p = new int(5);
    delete p;              // p now dangles
    *p = 6;                // undefined behavior
    
    int& bad() {
        int local = 42;
        return local;      // reference to a destroyed local
    }
    
    std::vector<int> v = {1, 2, 3};
    int* first = &v[0];
    v.push_back(4);        // may reallocate: first can dangle
    

    Avoid it with owning types (std::unique_ptr, containers), by returning by value, and by not keeping pointers into a vector across push_back. AddressSanitizer catches most cases.

  8. What is the this pointer in C++?

    A pointer, inside every non-static member function, to the object the function was called on. It separates a member from a parameter (this->x = x;) and lets a method return *this so calls chain.

    C++
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Query {
        string sql = "SELECT *";
    public:
        Query& from(const string& t) { sql += " FROM " + t; return *this; }
        Query& where(const string& c) { sql += " WHERE " + c; return *this; }
        string str() const { return sql; }
    };
    
    int main() {
        cout << Query().from("users").where("age > 18").str() << "\n";
    }

    This prints SELECT * FROM users WHERE age > 18. In a const member function this points to const.

  9. What is the difference between a function pointer and std::function?

    A function pointer holds the address of one function and carries no state; a captureless lambda converts to one. std::function<R(Args...)> is a type-erased wrapper that stores any callable with that signature: functions, capturing lambdas, functors.

    C++
    #include <functional>
    #include <iostream>
    using namespace std;
    
    int twice(int x) { return 2 * x; }
    
    int main() {
        int (*fp)(int) = twice;
        fp = [](int x) { return x + 1; };   // a captureless lambda converts
        function<int(int)> f = twice;
        cout << fp(5) << " " << f(5) << " ";
    
        int offset = 100;
        f = [offset](int x) { return x + offset; };   // a capturing one only fits here
        cout << f(5) << "\n";
    }

    It prints 6 10 105. The flexibility costs an indirect call that rarely inlines and possibly a heap allocation for large captures, and calling an empty std::function throws std::bad_function_call. For a callback used on the spot, a template parameter (template <class F> void each(F f)) is faster.

  10. Why can a const& bind to a temporary, and how long does the temporary live?

    Binding a temporary directly to a const T& (or a T&&) extends the temporary's life to the reference's scope, so const std::string& s = makeName(); is safe. A plain T& cannot bind to a temporary at all.

    C++
    #include <iostream>
    #include <string>
    using namespace std;
    
    struct Noisy {
        string name;
        explicit Noisy(string n) : name(n) { cout << "make " << name << "\n"; }
        ~Noisy() { cout << "destroy " << name << "\n"; }
    };
    
    Noisy make(string n) { return Noisy(n); }
    
    int main() {
        const Noisy& kept = make("kept");   // lives as long as the reference
        make("temp");                        // destroyed at the end of this line
        cout << "using " << kept.name << "\n";
    }

    It prints make kept, make temp, destroy temp, using kept, destroy kept. The extension applies only to that direct binding: a function returning a const std::string& to a local dangles, and so does const auto& n = makeObj().name(); when name() returns a reference into the temporary.

Coddy programming languages illustration

Learn to code with Coddy

Get started