0

In the following program, I declare a global variable (adj_matrix) for the purpose of using it in different functions. It is defined in another function (init_matrix).

I tested the program with the test case 3 1 2 and received a segmentation fault.

3 1 2
YES
Segmentation fault: 11

The surprising part is, that when I uncomment the cout line in the construct_matrix function, the segmentation fault disappears.

This looks like the case of undefined behavior to me but I'm unable to figure out why and where it occurs. Please help.

Following is the program:

#include <iostream>
#include <vector>

using namespace std;

vector<vector<int> > adj_matrix;

void init_matrix(int size, int val)
{
    adj_matrix.reserve(size);
    for (int i = 0; i < size; ++i)
    {
        adj_matrix[i].reserve(size);
        for (int j = 0; j < size; ++j)
        {
            if(i == j)
                adj_matrix[i][i] = 0;
            else
                adj_matrix[i][j] = val;
        }
    }
}

void construct_matrix(int size, int k, int val)
{
    // k denotes how many components we want
    for (int i = k - 1; i < size - 1; ++i)
    {
        adj_matrix[i][i + 1] = val;
        adj_matrix[i + 1][i] = val;
        // Uncommenting the following line resolves the seg-fault error
        // cout << i << endl;
    }
}

void print_matrix(int size)
{
    for (int i = 0; i < size; ++i)
    {
        for (int j = 0; j < size; ++j)
            cout << adj_matrix[i][j];
        cout << endl;
    }
}

int main()
{
    int n, a, b;
    cin >> n >> a >> b;

    /*
    The solution uses the fact that atleast one of G or G complement is always connected.
    In cases where we have to show both are connected (not possible when n is 2 or 3), 
    we draw a simple graph connected v1 v2 v3...vn. The complement will be also connected (n != 2 and 3)
    */

    if(a == 1 && b == 1)
    {
        if(n == 2 || n == 3)
            cout << "NO" << endl;
        else
        {
            cout << "YES" << endl;
            init_matrix(n, 0);
            construct_matrix(n, 1, 1);
            print_matrix(n);
        }
    }
    else if(a == 1)
    {
        cout << "YES" << endl;
        init_matrix(n, 1);
        construct_matrix(n, b, 0);

        print_matrix(n);

    }
    else if(b == 1)
    {
        cout << "YES" << endl;
        init_matrix(n, 0);
        construct_matrix(n, a, 1);
        print_matrix(n);

    }
    else
        cout << "NO" << endl;

    return 0;
}

For ones interested in the problem this is a solution to, visit here.

PS: I've checked the bounds in the for loop in my functions, and they are correct. If this wasn't the case, the program will throw a segmentation fault regardless of the cout line.

Laschet Jain
  • 734
  • 1
  • 8
  • 24
  • 1
    Can the downvoters explain what's wrong with my question? – Laschet Jain Jun 12 '18 at 10:07
  • Interestingly, [I can't reproduce your segmentation fault](https://ideone.com/6icica). Could you also show how your compiled the code? – andreee Jun 12 '18 at 10:10
  • It could be probably because `vector > adj_matrix;` is unitialized and then you try to access it in `init_matrix` and call `reserve` on the inner vector which therefore would cause a crash. – Vishaal Shankar Jun 12 '18 at 10:10
  • @andreee I used GNU g++. – Laschet Jain Jun 12 '18 at 10:11
  • @VishaalShankar It would be helpful if you can point out the exact details. I don't thing there is anything wrong with what I did. – Laschet Jain Jun 12 '18 at 10:11
  • C'mon, you can give me a little bit more :-)... command string, version, OS, whatever you think might be useful... – andreee Jun 12 '18 at 10:12
  • @andreee g++ 4.2.1 OS: MacOS Sierra 10.12.6 – Laschet Jain Jun 12 '18 at 10:13
  • @LaschetJain : The exact details are now as two answers :) – Vishaal Shankar Jun 12 '18 at 10:19
  • @VishaalShankar Yes. I always have misunderstood reserve function it seems. I still have to read the standard library containers chapters from the C++ book. Thanks for the help. – Laschet Jain Jun 12 '18 at 10:21
  • Probably just another point : That's why sometimes it's good to check `.empty()` on vectors, just to make sure that you are working on a container that has something. – Vishaal Shankar Jun 12 '18 at 10:22
  • For a quick help, google is probably faster than your C++ book. (Include "C++" and "std" in the key terms.) In this case, look specifically for hits on cppreference.com. It's probably one of the best ref. doc. sources online. – Scheff's Cat Jun 12 '18 at 10:23
  • @Scheff I do. Also, I agree to your point to a certain extent. But I've been reading C++ book for quite some time, it discusses certain things that we might leave unnoticed. Also, after reading the book, I've realized C++ is a very conceptual language and needs to be studied in a careful and comprehensive manner. – Laschet Jain Jun 12 '18 at 10:27
  • Sorry, if you got this wrong: To learn C++, a good C++ book is the first choice - I fully agree. To remember, how `std` things work in detail, I can warmly recommend cppreference.com. E.g. the `std::vector::data()` - can it be called for an empty vector? (Yes, it can but you may not dereference the result when vector is empty.) Such subtle details are always good for UB if not considered carefully - and there are too much that I can memorize them all. Therefore, I'm a frequent guest on cppreference.com. ;-) – Scheff's Cat Jun 12 '18 at 10:54
  • @Scheff Thanks. I agree with you. But I strongly feel that before exposing myself to cppreference.com I should first get all my basics with the book. But you're right. cppreference gives quite a lot of details. – Laschet Jain Jun 12 '18 at 11:04

2 Answers2

2

The reason / one of reasons of undefined behavior in this code is using operator[] to access elements of vector that are not yet created.

From http://www.cplusplus.com/reference/vector/vector/operator[]/:

A similar member function, vector::at, has the same behavior as this operator function, except that vector::at is bound-checked and signals if the requested position is out of range by throwing an out_of_range exception.

Portable programs should never call this function with an argument n that is out of range, since this causes undefined behavior.

reserve doesn't create new elements of the vector. It just ensures that the vector has allocated enough memory for them. If you want to create new elements which you can access, use resize. (https://stackoverflow.com/a/7397862/3052438)

I also recommend to double check all you vector accesses for out of bound indexes, after you fix the first problem.

Piotr Siupa
  • 3,929
  • 2
  • 29
  • 65
1

adj_matrix.reserve(size); Does not create any elements, but just reserves the size of the vector. As such adj_matrix[i].reserve(size); is undefined behaviour when i is greater than adj_matrix.size()

UKMonkey
  • 6,941
  • 3
  • 21
  • 30