-3

The code below a solution to the following requirement:

"Change the representation of Link and List from §27.9 without changing the user interface provided by the functions. Allocate Links in an array of Links and have the members: first, last, prev, and next be ints (indices into the array). "
- Exercise 6 Chapter 27 - Programming: Principles and Practice Using C++ B. Stroustrup

The interface is inherited from an ordinary implementation of an Intrusive doubly linked list. I've added the bool array (and the associated functions) to keep track of memory:

 #include <iostream>

struct Link
{
    int next;
    int prev;
};

//------------------------------------------------------------------------------------

struct List
{
    Link** head;
    int first;      // points to the current first node
    int last;

    bool* available;
    int list_size;

    int get_index()
    {
        for (int i = 0; i < list_size; ++i)
        {
            if (available[i] == true) 
            {
                available[i] = false;
                return i;
            }
        }
        throw std::bad_alloc("bla bla!\n");
    }

    List()
    {
        list_size = 30;

        head = new Link*[list_size];
        available = new bool[list_size];

        first = -1;
        last = -1;

        for (int i = 0; i < list_size; ++i)
        {
            available[i] = true;
        }
    }

    void List::push_back(Link* l)
    {
        if (l == nullptr)
        {
            throw std::invalid_argument("bla bla!\n");
        }

        int index = get_index();
        head[index] = l;

        if (last != -1)
        {
            head[last]->next = index;
            head[index]->prev = last;
        }
        else
        {
           first = index;
           head[index]->prev = -1;
        }

        last = index;
        head[index]->next = -1;
    }

    void push_front(Link* l)
    {
        if (l == nullptr)
        {
            throw std::invalid_argument("bla bla\n");
        }

        int index = get_index();
        head[index] = l;

        if (first != -1)
        {
            head[first]->prev = index;
            head[index]->next = first;
        }
        else
        {
            last = index;
            head[index]->next = -1;
        }

        first = index;
        head[index]->prev = -1;
    }

    // index = ptr - base
    std::ptrdiff_t index_from_address(Link* l) { return l - head[0]; }

    Link* front() const { return head[first]; }
};

//------------------------------------------------------------------------------------

int main()
{
    List l;

    for (int i = 0; i < 10; ++i)
    {
        l.push_back(new Link());
    }

    for (int i = 0; i < 10; ++i)
    {
        l.push_front(new Link());
    }

    std::cout <<"first = "<< l.first <<", index = " << l.index_from_address(l.front());
    getchar();
}

Expected result:

first = 19, index = 19

Actual result:

first = 19, index = 194

Why?

Ziezi
  • 6,375
  • 3
  • 39
  • 49
  • 1
    What did you find you with the debugger? – eerorika Jun 14 '17 at 14:14
  • 1
    I have no idea what sort of data structure this is supposed to be. – MSalters Jun 14 '17 at 14:16
  • 1
    Where do you initialize head[0] and head[first]? – Bob__ Jun 14 '17 at 14:17
  • Why does the first node have index 15? This code doesn't even make any sense. – Cody Gray - on strike Jun 14 '17 at 14:17
  • 2
    @Ziezi: But you don't have an array of `Link`. You have an array of `Link*`. – MSalters Jun 14 '17 at 14:18
  • to see 15 you will need to work on pointers to pointers: `std::ptrdiff_t index_from_address(Link** l) { return l - head; } Link** front() const { return &head[first]; }`, otherwise you work on uninitialized pointers – marcinj Jun 14 '17 at 14:22
  • @MSalters Is it OK now? – Ziezi Jun 14 '17 at 14:46
  • @CodyGray I would have to admit that the "M" in MCVE was a bit abused. – Ziezi Jun 14 '17 at 14:47
  • @Ziezi: I'm still struggling to understand what the datastructure should be. You really, really should learn the C++ standard containers first (in particular `std::vector<>` and `std::list<>`). The two main things you should be able to tell us about your container type are (1) what does it contain and (2) what operations does it support? – MSalters Jun 14 '17 at 14:53
  • @MSalters The above code is written as a solution to the following requirement: _" Change the representation of `Link` and `List` from §27.9 without changing the user interface provided by the functions. Allocate `Link`s in an array of `Link`s and have the members: `first`, `last`, `prev`, and `next` be `int`s (indices into the array). "_. The interface is inherited from an ordinary implementation of a doubly linked list. I've added the `bool` array (and the associated functions) to keep track of memory. – Ziezi Jun 14 '17 at 14:57
  • @Ziezi: If your book is having these exercises before introducing `std::vector`, ditch the book and check the [C++ book list](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). It sounds horrible, both on the C++ side and the pedagogical side. – MSalters Jun 14 '17 at 14:59
  • @MSalters Exercise 6 Chapter 27 - Programming: Principles and Practice Using C++ B. Stroustrup. I'm just writing it in C++ first, and then in C. – Ziezi Jun 14 '17 at 15:07
  • @MSalters I don't know if I've understood you well, when you were mentioning `std` containers...anyway, the memory management part will be placed in an allocator and separated from the List. This is just a first attempt. – Ziezi Jun 14 '17 at 21:26
  • @Ziezi: Well, that's a reasonable book from what I hear, and I'm pretty sure it covered `std::vector` before chapter 27. – MSalters Jun 14 '17 at 22:35
  • @MSalters the whole point of the exercise (IMO) is to compare implementations of Intrusive vs Non-instrusive containers, and to underline the additional memory management needs of the latter case (associated with container of pointers). – Ziezi Jun 15 '17 at 08:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/146734/discussion-between-msalters-and-ziezi). – MSalters Jun 15 '17 at 08:58

1 Answers1

3
l - head[0]

Here you compare the values of the two pointers. You let all pointers in the array be default initialized, so their values are indeterminate, and therefore the behaviour of accessing the values is undefined.

You probably intended index_from_address to find the index where a particular pointer object is stored - rather than the object that is pointed to, since the pointed to object is not in the array pointed by head. To do that, you must add a whole bunch of &:

 Link*& front() const // return a reference to the pointer object, not a copy

 //  take a reference to the pointer as an argument, add const for good measure
 std::ptrdiff_t index_from_address(Link*& l) const
// compare the addresses of the pointers, rather than values
 { return &l - &head[0]; }
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • From what I can understand this is a perfect answer to a absolutely reasonable question. Still can't understand why the down and close votes? Anyway, thank you! – Ziezi Jun 14 '17 at 15:17