This realization of linked list is broken. Address of nodes[0].next doesn't match the nodes[1] address. So nodes[1].next is NULL (as default value). I added some address printing to the search method. It looks like the nodes[1] wasn't initialized?
#include <iostream>
#include <vector>
using namespace std;
typedef struct Node_T {
int data;
Node_T *next;
} Node;
class LinkedList{
public:
vector<Node> nodes;
LinkedList(){
}
void insert(int data) {
Node temp_node;
temp_node.data = data;
temp_node.next = NULL;
size_t len = nodes.size();
nodes.push_back(temp_node);
if (len > 0) {
nodes[len - 1].next = &nodes[len];
}
}
int search(int val){
if (nodes.empty())
return -1;
Node *node_ptr = &nodes[0];
// Debug
cout << &nodes[1] << "\n";
cout << &nodes[0].next << "\n";
int i = 0;
do {
if (node_ptr->data == val) return i;
i++;
} while((node_ptr = node_ptr->next) != NULL);
return -1;
}
};
int main()
{
LinkedList llist;
llist.insert(1);
llist.insert(2);
llist.insert(3);
llist.insert(4);
llist.insert(5);
cout << llist.search(3) << "\n";
return 0;
}
It shows me: 0x8e6a060 0x8e6a05c -1
When you add elements to a vector, references to (and hence addresses of) vector elements are invalidated. You must therefore not use values such as &nodes[0] or &nodes[len], as they are meaningless.
The point with an exercise like this is to get the hang of the internal structure in a linked list. You have replaced that internal structure with a vector<Node>.
Instead of a vector, the idea is to have a
private:
Node* head;
As you data member.
In your insert function you are supposed to dynamically allocate memory for the Node with
Node* newNodePointer = new Node;
And manipulate the pointer with next and such.
It is worth to point out, that this is fine as an exercise, but your "real" code should use standard library facilities.
First, Your printout is incorrect: this line
cout << &nodes[0].next << "\n";
prints the address of next, rather than printing the next itself. Changing to
cout << nodes[0].next << "\n";
gives the correct printout (demo).
However, the main issue is that you keep pointers to elements of std::vector. These become invalid after the first write, because new storage gets allocated for the growing vector.
You can certainly work around this by reserving sufficient space upfront (call nodes.reserve(1000) from the constructor of your list; demo) but that is merely a hack: you should use new and delete to allocate elements of your linked list manually. That is the whole point of this exercise.
But I still need a container to ensure that nodes will be live as expected?
No, you do not. Your class is a container. By referencing the whole chain of nodes from the head pointer it can ensure that the entire chain is kept "live".
Related
I am writing an auction program for a class project and one of the features I was trying to implement was a hash table to make searching for auction items by name efficient. I set it up in node format so that you can chain nodes together if their hash value lines up with another item that already exists.
The main problem that I cannot seem to figure out is how some pointer values are changing when I don't think I have done anything to them. I stepped through each line of this program keeping an eye on the Red highlighted areas in the attached screenshots to see when the data changes. In case #1 the data was intact and able to be accessed. However, in case #2 where I simply declare an additional variable (int i = 0;) suddenly the data passed into the function appears to point to a different memory location (0xcccccccc) which from what I understand is another version of null? This is the same no matter what variable type I have tried to declare whether it be an int, const char*, string, etc it all reacts like the second screenshot.
Does anyone know why the program would be doing this? Are there any other troubleshooting tips? Is this a common error and how should I avoid it in the future and for this project?
I can provide a complete code if needed. I appreciate any help you can provide.
Image 1: No additional variable declared, data in tact as expected
Image 2: integer variable declared, data at ->next suddenly changed. This appears to be this way from the start of the function.
Update: I created an MRE as suggested in a comment, the same error can be reproduced using this code.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
class AuctionItemBidsMaxHeap {
string name = "test";
public:
const char * getItemName() {
return name.c_str();
}
};
class AuctionItemHashTable {
private:
struct Node {
AuctionItemBidsMaxHeap* AuctionItem;
Node* next = nullptr;
};
Node* itemArray;
int capacity = 50;
int generateHashKey(string auctionItem) {
return 11;
}
public:
AuctionItemHashTable() {
//Create the array of X amount of different possible storage locations
Node emptyNode;
emptyNode.AuctionItem = nullptr;
emptyNode.next = nullptr;
itemArray = new Node[capacity];
for (int i = 0; i < capacity; i++) {
itemArray[i] = emptyNode;
}
}
~AuctionItemHashTable() {
delete itemArray;
}
void insertItem(AuctionItemBidsMaxHeap* auctionItem) {
//Check to see if this item already exists
int key = generateHashKey(auctionItem->getItemName());
Node newAuctionItem;
newAuctionItem.AuctionItem = auctionItem;
newAuctionItem.next = nullptr;
//Check to see if anything has been inserted there yet
if (itemArray[key].AuctionItem == nullptr) {
itemArray[key] = newAuctionItem;
}
else {
//WE have to make room in the semi-linked list
Node holder;
holder.AuctionItem = itemArray[key].AuctionItem;
holder.next = itemArray[key].next;
newAuctionItem.next = &holder;
itemArray[key] = newAuctionItem;
}
}
AuctionItemBidsMaxHeap* getAuctionItem(const char* itemName) {
int key = generateHashKey(itemName);
//Loop through all items in location
Node* currentNode = &itemArray[key];
if (currentNode == nullptr) {
return nullptr;
}
else {
if (currentNode->AuctionItem->getItemName() == itemName) {
cout << "Match" << endl;
}
while (currentNode->next != nullptr && currentNode->next != (void*)0xcccccccc) {
int i = 0;
if (currentNode->next->AuctionItem->getItemName()[0] == 'M') {
cout << "M Matched" << endl;
}
while (currentNode->next->AuctionItem->getItemName()[0] != 'e') {
//cout << currentNode->next->AuctionItem->getItemName()[i];
}
currentNode = currentNode->next;
}
//There was an item stored at this location, lets see which one it is
//void* p = (void*)0xcccccccc; //Creating a pointer since for some reason my final pointer gets changed to another type of null character upon passing it to a function
//cout << currentNode->AuctionItem->getItemName() << endl;
//while (currentNode->next != nullptr && currentNode->next != p) {
//cout << currentNode->AuctionItem->getItemName() << endl;
//currentNode = currentNode->next;
//}
return currentNode->AuctionItem;
}
}
};
int main()
{
/**Creating MaxHeap of one bid**/
AuctionItemBidsMaxHeap myBidTest;
AuctionItemBidsMaxHeap myBidTest2;
/**Creating Auction Item Hash Table**/
AuctionItemHashTable auctionItems;
auctionItems.insertItem(&myBidTest);
auctionItems.insertItem(&myBidTest2);
const char* myInput = "test";
auctionItems.getAuctionItem(myInput);
}
First a rant: Why is it that classes still teach pointers in C++? There are MUCH better ways to do this than Node*.
Your code contains several errors, but the most important one is here:
//WE have to make room in the semi-linked list
Node holder;
holder.AuctionItem = itemArray[key].AuctionItem;
holder.next = itemArray[key].next;
newAuctionItem.next = &holder; ////<<< ERROR HERE
itemArray[key] = newAuctionItem;
You create a temporary variable on the stack Node holder; This variable will be destroyed as soon as you leave the function.
But you take a pointer to this variable here
newAuctionItem.next = &holder;
IOW: Your list contains pointers to objects that no longer exist.
&holder is the address of the variable holder. As soon as holder goes out of scope, the contents of it will be destroyed. But newAuctionItem.next and as a consequence also itemArray[key].next will still point to the memory, where holder used to be.
This is what is called a dangling pointer.
I stopped reading your example, but it is also pretty dangerous to accept pointers to AuctionItems in your insert method. When you are using pointers here, you MUST MAKE SURE, that the actual objects remain valid for as long as they are in the list.
Or, to put it the other way round: You must remove them from your list before they get destructed. And we humans are not made to "make sure". We make errors, so it is better to write code where you cannot make an error like this (i.e. avoid pointers in the first place).
Another error: You are creating an array with itemArray = new Node[capacity];, but you are deleting it with delete itemArray;. When you are using new to create an array, you must use delete[] itemArray to delete it. See here delete vs delete[] operators in C++
A general note: DO NOT USE POINTERS AT ALL (unless you have to). Pointers are an advanced C++ concept.
You could use shared_ptr<> instead. This will take away the burdon of freeing the memory.
For your itemArray you could use std::vector<> instead of allocating an array with new[]; etc...
There are many good and easy to use classes in the C++ library, which will help you a lot writing safer and cleaner code.
Learning C++ is (at least) as much about learning the std Library as about learning the syntax and statements. std::vector<AuctionItemNodes> IS C++.
This problem seems like a trivial one but I am unable to get my head around it.
I am trying to add a node by passing in a root element to a function insert. The function first checks if the current root is empty, if it is, it simply creates a new node and set it as a root. My first attempt was something like this
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node(int x) {
data = x;
}
};
void insert(Node *node, int x) {
cout << &node << "\n";
if(node == NULL) {
node = new Node(x);
}
}
int main () {
Node *aNode;
aNode = NULL;
insert(aNode, 8);
cout << aNode << "\n";
return 0;
}
This of course didn't work since I was passing pointers by value which was causing aNode to be still NULL and with some googling I came to know I need to pass pointers by reference, my second attempt was something like this
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node(int x) {
data = x;
}
};
void insert(Node **node, int x) {
if(*node == NULL) {
*node = new Node(x);
cout << *node->data << "\n"; //gives error but program runs fine without it
}
}
int main () {
Node *aNode;
aNode = NULL;
insert(&aNode, 8);
cout << aNode->data << "\n";
return 0;
}
Now that worked without the cout in insert function since the cout in main printed 8 which is what I wanted but compiler gave error request for member 'data' in '* node', which is of pointer type 'Node*' (maybe you meant to use '->' ?) while at cout of insert. I changed the insert as follows to run some tests and results were confusing
void insert(Node **node, int x) {
if(*node == NULL) {
Node *bNode = new Node(x);
cout << bNode << "\n"; // prints address of bNode
cout << *node << "\n"; // prints NULL which is correct
*node = bNode;
cout << *node << "\n"; // prints the same address as of bNode
cout << bNode->data << "\n"; // prints 8
cout << *node->data << "\n"; // gives error WTF!!!
}
}
Can somebody please explain?
#RemyLebeau comment was spot on and pointed out the exact problem. So here is the solution.
Problem Background
So I had a pointer to an object of type Node aNode and had a function insert(Node *node, int x). I wanted the function insert to check if given pointer to node doesn't point to anything then create a new node and set the reference in provided node. The expectation was that after setting the reference aNode will point to the newly created node as well. However it was a false expectation because I was passing the pointer by value instead of reference which meant node in the function insert was a different one than aNode
Buggy Solution
So the solution was to pass reference of aNode to function insert. So I changed my function call to insert, to this
insert(&aNode, 8);
And change the function definition of insert to this
void insert(Node **node, int x)
That fixed the problem, since **node was now dereferened to the address of aNode and I was able to get its value by simply dereferencing node by *node. The problem occured when I tried to access members of Node by *node->data. The reason was that * and -> are actually operators and -> has higher precedence than *. So while evaluating *node->data it was actually executing in this way *(node->data) which means compiler was actually trying to dereference node->data instead of node. The quick solution was just to access data like this (*node)->data.
Right Solution
Although through parenthesis I was able to achieve the desired behavior but the syntax was ugly. The right solution as #pm100 pointed to use reference in function definition instead of dereferencing it twice which means declare function like this
void insert(Node *&node, int x)
instead of like this
void insert(Node **node, int x)
That enabled to access node members simply like node->data instead of (*node)->data
If you are new to pointers like me do watch https://www.youtube.com/watch?v=Rxvv9krECNw
I am currently reading a book on data structures and learning c++ on the side. I am trying to implement a simple linked list. Below is some code for a list that can take a maximum of two elements (in order to isolate my problem).
What is going wrong is the pointer declaration to the next node in the list. When I create a new Node instance and create a pointer to it, the pointer stays the same on each method call, so all elements in the list point to the same node. However, if I create a pointer directly, everything works as expected.
What I am guessing is that I have some fundamental misunderstanding of pointers, references and the new keyword.
Feel free to run the code below. The working code is commented out.
#include <iostream>
using namespace std;
template <typename T> class Node {
public:
Node(T nvalue) {
this->value = nvalue;
this->next = NULL;
}
T value;
Node *next;
};
template <typename T> class LinkedList {
public:
Node<T> *head;
LinkedList() {
this->head = NULL;
}
void append(T newVal) {
// Correct
// Node<T>* newNode_ptr = new Node<T>(newVal); // newNode_ptr is different on each call
// Incorrect!?
Node<T> newNode = Node<T>(newVal);
Node<T> * newNode_ptr = &newNode; // newNode_ptr is the same on each call
cout << "New Node Address: " << newNode_ptr << endl;
if (!(this->head)) {
this->head = newNode_ptr;
cout << "Value 0: " << this->head->value << endl;
} else {
this->head->next = newNode_ptr;
cout << "Value 0: " << this->head->value << endl;
cout << "Value 1: " << this->head->next->value << endl;
}
}
};
int main() {
LinkedList<int> list = LinkedList<int>();
list.append(21);
cout << "..." << endl;
list.append(42);
}
Note that this code is not exactly well designed (some stuff should be private, using namespace std should be avoided). I am familiar with python so this pointer stuff is a little overwhelming. Thanks for your help in advance!
Node<T>* newNode_ptr = new Node<T>(newVal);
This is the more correct way of the two. It is normal that the address of newNde_ptr is different, it's what you want. Each node is a different node, two different objects cannot have the same address! The version without new gives the same address because you are creating the objects on the stack. This will not work, every node is destroyed at the end of the append function. You will see unusual results (if it doesn't crash) if you move the printing portion of append to another function. Since all your pointers point to the same address (in your case) and at the point where you print out the values that address just so happens to be a valid Node, you do not see a crash. However, this is undefined behavior and can change for any number of reasons.
The difference between free-store (heap for malloc/free) and the stack is a fundamental concept of c++. You should read about it here.
The reason I saw more correct way of the two is that you still have to remember to delete your nodes. A better way would be to use std::unique_ptr instead of raw pointers to avoid that (and may other) mistakes that using raw pointers encourages.
// Node * next; becomes
std::unique_ptr<Node> next;
// Node<T> newNode = Node<T>(newVal); becomes
newNode = std::make_unique<T>(newVal);
I'm new to c++ and tried to get familiar with the language by implementing a LinkedList.
class ListElement {
public:
int val;
ListElement *next;
ListElement(int v, ListElement *n) {val = v; next = n;};
};
ListElement contains an int value val and a pointer to the next list element (nullptr if there is no next element) and a constructor.
class MyLinkedList {
public:
ListElement *head;
MyLinkedList() {head = nullptr;};
ListElement* getHead(void){
return head;
};
void append(int i) {
head = &ListElement(i, head);
};
};
MyLinkedList contains a pointer to the first element of the list named head as well as some methods working on the list. Encountering some bugs in those methods I tried to track down their cause. (I'm aware that a getter for a public class member makes no sense at all, originally head was private.) Doing so I observed the following behaviour I can't explain:
int main() {
MyLinkedList l;
l.append(1);
int x = l.head->val;
cout << "head: " << x << "\n";
int y = l.getHead()->val;
cout << "getHead(): " << y << "\n";
int z = l.head->val;
cout << "head: " << z << "\n";
cin.get();
return 0;
}
Running this code (add #include <iostream> and using namespace std; for a working example) prints
head: 1
getHead(): 18085840
head: -858993460
so the first direct access of head works just as expected, yielding 1 as value of the first list element, but using the getter returns garbage. If head is then accessed directly again, it also yields garbage, which made me think "Hm, seems like using getHead() somehow garbles the ListMember object", just to discover that
int x = l.head->val;
cout << "head: " << x << "\n";
int z = l.head->val;
cout << "head: " << z << "\n";
prints
head: 1
head: -858993460
without even touching the getter. So is simply accessing l.head in any way enough to garble it?
No, as
int x = l.head->val;
int z = l.head->val;
cout << "head: " << x << "\n";
cout << "head: " << z << "\n";
returns (as intended) head: 1 two times. So using cout in between changes my objects or their pointers? And what's wrong with getHead() as all it does is just return head;?
So I'm pretty lost here and couldn't find any directly related questions. (This Question has a promising title but doesn't use pointers). Am I totally failing to use pointers in a correct way? Or is some automatic object deletion going on behind the scenes? Or is this just how magiC++ works?
In
void append(int i) {
head = &ListElement(i, head);
};
ListElement(i, head) creates a temporary, nameless ListElement and assigns a pointer to it to head. Then, because the ListElement itself wasn't assigned to anything, the ListElement goes out of scope and is destroyed. This leaves head pointing to invalid memory.
Head could be written to use dynamic memory to extent the life of the ListElement
void append(int i) {
head = new ListElement(i, head);
};
but now someone has to pick up the responsibility of ensuring that the ListElement is deleted when no longer required.
For example:
void remove(int i) {
// find list element i and previous element. Special handling required for first element
prev.next = element.next;
delete element;
};
Careful use of std::unique_ptr and std::move can get automate the memory management and eliminate the need for delete.
In C++ you must manage your own memory. The statement
ListElement(i, head);
Creates an instance of ListElement locally scoped the MyLinkedList::append(). Thus once that function exits the variable no longer exists and the pointer is now pointing at invalid memory.
The reason your first print statement is giving you a seemingly correct answer is a bit of a red herring. In all cases you are accessing free-ed memory which has undefined behavior. In the first case that memory just happens to have the value that you previously set.
You have to allocate your own memory in append and clean it up when you are done with it. Once you have mastered "new" be sure to look up how to iterate through a data structure and delete each element. With your linked listed implementation this should be fairly trivial.
change
void append(int i) {
head = &ListElement(i, head);
};
to
void append(int i) {
head = new ListElement(i, head);
};
The first, if it compiles, is taking the address of a temporary stack allocated object. Hence head will "point at garbage" after it's destruction.
I have TWO implementations of this.
Why does this particular implementation NOT work? I have a pointer to a pointer and im changing the inside point but it doesn't retain the change in the main function
#include <iostream>
using namespace std;
struct Node {
int value = 4;
Node* next;
};
void insertFront(Node*, Node**);
int main(){
Node head;
head.value = 32;
head.next = nullptr;
Node** headptr = new Node*;
(*headptr) = new Node;
*(*headptr) = head;
(*headptr)->value = 32;//redundant, I know
(*headptr)->next = nullptr;//redundant, I know
cout << head.value << endl;
insertFront(new Node, headptr);
cout << head.value << endl;
system("pause");
return 0;
}
void insertFront(Node* newHead, Node** head2){
cout << "Inside before swap " << (*head2)->value << endl;
newHead->next = *head2;
*head2 = newHead;
cout << "Inside after swap " << (*head2)->value << endl;
}
Why does this one work? Can someone please explain IN DETAIL the pointer magic going on? I have a vague idea but im still a little bit confused. I understand that using a pointer to the head pointer allows you to change the head address globally but it's still a little bit cloudy. Can someone please clarify, what is going on with these pointers in both implementation?
#include <iostream>
using namespace std;
struct Node {
int value = 4;
Node* next;
};
void insertFront(Node*, Node**);
int main(){
Node** head = new Node*;
(*head) = new Node;
(*head)->value = 32;
(*head)->next = nullptr;
cout << (*head)->value << endl;
insertFront(new Node, head);
cout << (*head)->value << endl;
system("pause");
return 0;
}
void insertFront(Node* newHead, Node** head2){
cout << "Inside before swap " << (*head2)->value << endl;
newHead->next = *head2;
*head2 = newHead;
cout << "Inside after swap " << (*head2)->value << endl;
}
Both implementations are using double-indirection wrong, and both leak memory. You're question seems more about double-indirection than just about what works and what doesn't (whether you realize it or not). Its a C question, and though also applicable in C++, it is less so with that language because reference parameters make this somewhat easier (arguably).
I could simply say "use references to pointers" (which you could do), but that would be like you saying "why doesn't my car work?" and me answering "because this car over here will work". So I will provide a C answer (much to the dismay of my own common sense, as I can feel the furnaces firing up from the flamethrowers about to be sent my way). If I have time, I will include the C++ answer (using references), but no guarantees on that.
Pointers to pointers are no different than any other pointer type. All pointer types are types who's variables are defined to "point" to something of that type (I know, its repetitive and trivial, but bear with me here). The trivial example:
void foo(int x)
{
x = 5;
}
obviously doesn't change x on the caller side, and you seem keenly aware of that. If you want to change an in/out parameter using pointers, you need to declare the formal parameter to be a pointer-to type, dereference said-pointer parameter within the function body, and pass the address from the caller. Ie.
void foo(int *p)
{
*p = 5;
}
int main()
{
int x = 0;
foo(&x);
}
The truth is parameters are all pass-by-value in C, even pointer parameters. Yeah, read that again. You say what? Seriously. Its true. It just so happens the "value" you're passing is an address rather than the value within some variable, and in so being as such, the receiver must be something that is prepared to take, and manipulate, data via that address: a pointer.
Now. Pointers to pointers are no different. Pointers to pointers hold addresses of (wait for it...) pointers. Just like our first example, this:
struct Node
{
int data;
struct Node *next;
}
vod foo(Node* ptr)
{
Node *p = new Node();
p->data = 0;
p->next = ptr;
ptr = p;
}
int main()
{
Node *root = nullptr;
foo(root);
}
won't work. You can fix this several ways. One way is using a pointer-to-pointer (the C way). Another uses a reference to pointer (the C++ way).
First the C way, which demonstrates the whole mantra of passing something by address means declaring the parameter to be a pointer-to type (in this case a pointer to pointer type), and passing the address of the thing to modify:
void foo(Node** ptr)
{
Node *p = new Node();
p->data = 0;
p->next = *ptr;
ptr = p;
}
int main()
{
Node *root = nullptr;
foo(&root); // LOOK: passing address of our root pointer
}
Do you see how, just like in our trivial example using int and int*, we have to pass the address of the thing we're modifying to a function that takes a pointer-to-type? In this case the "type" is, itself, a pointer type.
Now, arguable, the C++ way using a reference is trivial by comparison, but IMHO it isn't as clear what is going on, only because there is literally a single character difference between the version that doesn't work and the version that does. Look at this:
vod foo(Node*& ptr) // LOOK added &
{
Node *p = new Node();
p->data = 0;
p->next = ptr;
ptr = p;
}
int main()
{
Node *root = nullptr;
foo(root);
}
Notice how everything else in this is identical to the version that does not work. Everyone has their preferences, and knowing what to look for allows me to use either method, but I can see why some have such difficulty writing and debugging what is essentially double-indirection code hidden in a reference type. Some engineers prefer to send all their out-params as pointer-to types, and I'm generally one of them.
Peeling Back Your Code
After all of that, lets peel back your code and see where things go to hell. I'll dissect the one that does not work, and hopefully you can see why neither version is honestly very good:
First your type:
struct Node
{
int value = 4;
Node* next;
};
Nothing horridly questionable here. The default value assignment in the structure definition. This will puke on non-current-day C++, so likely throw that out for now. If you want a default value, make a constructor (which you should have anyway to ensure all members are properly initialized to something):
struct Node
{
int value;
Node* next;
Node(int val = 4)
: value(val)
, next()
{}
};
Ok. Next up..
void insertFront(Node*, Node**);
You seem to want to use a pure node interface. Most people writing a linked list would do this:
void insertFront(Node** ppRoot, int value);
but we'll go with your version for now. The actual implementation of this:
void insertFront(Node* newHead, Node** head2)
{
newHead->next = *head2;
*head2 = newHead;
}
is correct. Yes it could orphan anything previously being pointed to by newHead->next, but that doesn't seem to be a concern of yours, so we go with it for now.
Finally the torrent: main().
int main()
{
Node head;
head.value = 32;
head.next = nullptr;
Node** headptr = new Node*;
(*headptr) = new Node;
*(*headptr) = head;
(*headptr)->value = 32;//redundant, I know
(*headptr)->next = nullptr;//redundant, I know
cout << head.value << endl;
insertFront(new Node, headptr);
cout << head.value << endl;
system("pause");
return 0;
}
This has multiple issues. First, your mixing dynamic nodes with non-dynamic nodes.
Node head;
head.value = 32;
head.next = nullptr;
Bad idea. This is no reasonable way calling code (in particular cleanup code that deletes each node from the list), has any clue whether something being pointed to is dynamic or not. Don't do that.. Using the constructor version of Node from above, this should simply be:
Node* head = new Node(32);
Next you're dynamically allocating a pointer; (not a Node; a pointer)
Node** headptr = new Node*;
Bad idea. there is no need to do that at all. You already have a pointer variable to your list head (its called, not-coincidentally, head). This appears all to be a setup for invoking the insertion function. To do that, everything from Node** headptr = new Node*; on down can simply be replaced with this:
insertFront(new Node(10), &head); // LOOK: passing address of head pointer
cout << head->value << endl;
The way you are using your pointers is so, so wrong.
Let's look at this code:
Node** headptr = new Node*;
(*headptr) = new Node;
*(*headptr) = head;
(*headptr)->value = 32;//redundant, I know
(*headptr)->next = nullptr;//redundant, I know
cout << head.value << endl;
insertFront(new Node, headptr);
cout << head.value << endl;
Let's first clean up this code a bit. There is no reason to allocate a Node * on the free store (using new), and then reference it through a Node **. It can and should simply be a local variable and referenced directly. To do that, we replace Node** headptr = new Node*; with simply Node *phead, and replace all instances of (*headptr) with merely phead:
Node* phead;
phead= new Node; // #2
*phead= head; // #3
phead->value = 32;//redundant, I know
phead->next = nullptr;//redundant, I know
cout << head.value << endl;
insertFront(new Node, &phead); // here we are passing the address of phead so that insertFront() can modify it
cout << head.value << endl;
Now look at this code carefully. You allocated space for a new Node on line 2, and made phead point to it. You copied the contents of head into this new Node on line 3. Then your insertFront() call modified a newly allocated node and set phead to point to that new node instead. At no point did any pointer ever point to head, and its value is never touched; when you check head.value, of course they remain the same.