Common C++ Programming Errors and Memory Pitfalls

Classified in Computers

Written on in English with a size of 6.93 KB

L-Values, R-Values, and Stack Variables

An l-value can be modified, while an r-value cannot be accessed again. Additionally, a stack variable is destroyed when it is popped from the stack.

Container Errors and Undefined Behavior

std::list Indexing Error

Code:

list<int> li;
li.push_front(5);
cout << li[1] << endl;

Error: The subscript operator or function is not defined for this type. std::list does not have a [] operator because it does not support random access.

std::vector Out-of-Bounds Access

Code:

vector<string> v;
string h = "hello";
v[0] = h;
cout << v[0] << endl;

Error: Indexing out of the bounds of the container. Since the vector is empty, this behavior is undefined.

Pointer and Reference Pitfalls

Uninitialized Pointer Arithmetic

Code:

int arr[5] = {1, 2, 3};
int i;
int *p = arr + i;

Error: The variable i is never initialized, leading to illegal and unsafe pointer arithmetic.

Reference Assignment

Code:

int i = 15;
int j = 30;
int& k = j;
int* m = &i;
k = *m;

Result: Both i and j become 15. Assigning *m to the reference k modifies the underlying variable j.

Const References

Code:

int i = 50;
const int& k = i;
i = 50;

Result: Both i and k are 50. However, if you attempt to change the value directly through k (or if k was a pointer and you tried to change the value it points to), it will result in a compilation error.

Constant Pointers

Code:

int arr[5] = {1, 2, 3};
int* const p = arr + 2;
cout << *p;
++p;
cout << p;

Error: p is a const pointer (int* const). You cannot change what p points to, so incrementing p (++p) causes a compilation error.

Pointer to Const Assignment

Code:

int i = 10;
const int j = 100;
int* p = &i;
*p = 50;
p = &j;
cout << p;

Error: You cannot assign the address of a const variable (&j) to a non-const pointer (int* p). Doing so would allow modifying the constant value of j through the pointer, which is illegal.

Uninitialized Pointer Dereferencing

Code:

int i = 100;
int* p;
cout << *p;

Error: The pointer p is not initialized. Dereferencing an uninitialized pointer is illegal and causes undefined behavior.

String Literal Assignment to char*

Code:

char *m = "midterm";

Error: "midterm" is a string literal (type const char*), not a single char. Assigning a string literal directly to a non-const char* is invalid in modern C++ and can lead to compilation errors.

Pointer Arithmetic Evaluation

Code:

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
int *m = arr + 2;
cout << *(m + *p);

Result: Evaluates to 4. Since *p is 1, m + *p points to arr[3], which has a value of 4.

Vector Copy Behavior

Code:

int i = 10;
int j = 100;
int k = 1000;
vector<int> v;
v.push_back(i);
v.push_back(j);
v.push_back(k);
v[0] = 11;
v[1] = 111;
v[2] = 1111;
cout << "i:" << i << " j:" << j << " k:" << k;

Result: Outputs i:10 j:100 k:1000. Modifying the elements inside the vector does not affect the original variables because push_back stores copies of the values.

Exception Handling in C++

Uncaught Exceptions

Code:

void func() {
    throw "exception";
}

int main(int argc, char *argv[]) {
    try {
        func();
    } catch(std::exception &e) {
        cout << "caught exception" << endl;
    }
    catch (int &i) {
        cout << "caught int" << endl;
    }
    catch (string &s) {
        cout << "caught string" << endl;
    }
    return 0;
}

Error: Not all exceptions are caught. Throwing a string literal (const char*) will not be caught by std::exception, int, or std::string. You need a catch(...) block to catch any unhandled exception types.

Catch-All Exception Block

Code:

void func() {
    throw -1;
}

int main(int argc, char *argv[]) {
    try {
        func();
    } catch(std::exception &e) {
        cout << "caught exception" << endl;
    } catch(unsigned int &i) {
        cout << "caught uint" << endl;
    } catch(string &s) {
        cout << "caught string" << endl;
    } catch(...) {
        cout << "caught unknown" << endl;
    }
    return 0;
}

Result: Outputs caught unknown. The thrown value is a signed int (-1), which does not match unsigned int, std::string, or std::exception, so it falls back to the catch-all catch(...) block.

Structs and Function Overloading

Missing Operator Overloading

Code:

struct Foo {
    int x;
    int y;
    Foo(int i, int j) : x(i), y(j) {}
};

int main(int argc, char* argv[]) {
    Foo F1(1, 2);
    Foo F2(2, 2);
    if (F1 != F2) {
        cout << "no match" << endl;
    } else {
        cout << "match" << endl;
    }
    return 0;
}

Error: The inequality operator (!=) is undefined for Foo because no comparison operators are defined inside the struct. The compiler does not know how to compare the two objects.

Ambiguous Function Overloading

Code:

int sum(vector<int> &v);
int sum(vector<int> &v, int offset = 0);

int main(int argc, char* argv[]) {
    vector<int> v;
    v.push_back(10);
    cout << sum(v);
    return 0;
}

Error: This causes a compilation error due to ambiguity. The call sum(v) can refer to both functions because the second function has a default argument for offset, making both signatures valid candidates.

Incomplete Class Definition

class Bar {
    // Incomplete class definition
};

Related entries: