I wrote a simple function which finds the oldest person in an array of structs. The structure stores information about age and name.
#include <iostream>
struct Person
{
int age;
char name[16];
};
char* oldest(Person* arr, int len)
{
int max = 0;
char* ptr = nullptr;
Person elem;
for (int i = 0; i < len; i++)
{
elem = arr[i];
if (max < elem.age)
{
max = elem.age;
ptr = arr[i].name;
}
}
return ptr;
}
int main()
{
Person list[3] = {
{20, "Alice"},
{70, "Bob"},
{25, "James"}
};
std::cout << oldest(list, 3) << '\n';
}
It yields correct result (namely I see Bob on the screen), but when I use elem instead of arr[i] in the line ptr = arr[i].name; (which is nothing but giving another name to arr[i], right??) the program suddenly starts giving some weird results (unprintable characters). I have no idea why it behaves this way.
For your reference, this is the code that doesn't work:
char* oldest(Person* arr, int len)
{
int max = 0;
char* ptr = nullptr;
Person elem;
for (int i = 0; i < len; i++)
{
elem = arr[i];
if (max < elem.age)
{
max = elem.age;
ptr = elem.name;
}
}
return ptr;
}
ptr = elem.name; assigns to ptr the address of the first element of elem.name (because the arary elem.name is automatically converted to a pointer to its first element). elem.name is of course an array inside elem, and elem is an object with automatic storage duration, meaning it is created automatically in the block it is defined in and it is destroyed when execution of that block terminates. So, when the function return, elem ceases to exist in the C++ model of computing, and a pointer to part of it becomes invalid.
… which is nothing but giving another name to arr[i], right??
No, the statement elem = arr[i]; makes a copy of arr[i] in elem. It does not make elem an alternate name for arr[i]. That copy ceases to exist when the function returns.
If you removed the Person elem; declaration and, inside the loop, used Person &elem = arr[i]; instead of elem = arr[i];, that would define elem to be a reference to arr[i]. Then it would be effectively an alternate name for arr[i], and ptr = elem.name; would set ptr to point to the first element of arr[i].name.
(which is nothing but giving another name to arr[i], right??)
Not right. elem is a distinct object. It is not a name of arr[i].
but when I use elem instead of arr[i] in the line ptr = arr[i].name; ... the program suddenly starts giving some weird results
With that change, you are returning a pointer to (a member of) an automatic variable. When the function returns, the automatic variable is destroyed and the returned pointer will be invalid. When you indirect through the invalid pointer and attempt to access deallocated memory, the beahviour of the program is undefined.
Related
I've been trying to create a function that turns an array into a linked list. It creates an array of nodes the same length as the array and stores each value in the array into the data field of each node, and sets up the pointers similarly. I quickly realized it wasn't working since print() is giving undefined results after printing the data value of the first node. I used some cout calls to see what's going on and found that the two cout calls in main, even though exactly the same, produce different results on the MSVC2019 compiler. The first one produces 2 (as it should) and the second one produces an undefined result. Below is the full program:
#include <iostream>
struct Node {
int data;
Node* next;
};
void print(Node n);
Node toNode(int ar[], int size);
int main(void) {
int ar[] = { 1, 2, 3, 4, 5 };
Node n = toNode(ar, 5);
std::cout << (*(n.next)).data << std::endl;
std::cout << (*(n.next)).data << std::endl;
// print(n);
}
Node toNode(int ar[], int size) {
Node nodes[100];
for (int i = 0; i < size; i++) {
Node n;
n.data = ar[i];
nodes[i] = n;
}
for (int i = 1; i < size; i++) {
nodes[i-1].next = &nodes[i];
}
nodes[size-1].next = NULL;
for (int i = 0; i < size; i++) {
Node n = nodes[i];
}
return nodes[0];
}
void print(Node n) {
std::cout << n.data << std::endl;
if (n.next != NULL)
print(*n.next);
}
here's the output on MSVC 2019:
I tried the same code on an online compiler and it gave a consistent 2 each time:
this is what I get when I call print(n) on MSVC2019:
is there something wrong with the toNode() function? I tried printing out the values for each node in the nodes[] array and they were 1, 2, 3, 4, and 5. I also tried printing out the value of the node that's being pointed by each node in the nodes[] array, and the results were 2, 3, 4, and 5 as expected.
also do take note there is a warning on the line
nodes[size-1].next = NULL;
in the toNode() function. The warning message is:
C6386: Buffer overrun while writing to 'nodes': the writable size is '552' bytes, but '-8' bytes might be written.
This is a very common problem.
The issue is that your code is using pointers to objects which have been destroyed. Take a look at your toNode function (with comments)
Node toNode(int ar[], int size) {
Node nodes[100];
...
for (int i = 1; i < size; i++) {
nodes[i-1].next = &nodes[i]; // pointers to 'nodes' being saved here
}
...
return nodes[0]; // object containing pointers to 'nodes' returned here
}
The problem is that the nodes array is destroyed when toNode is exited. So all those pointers you have saved are pointing to objects that have been destroyed. This explains the inconsistent behaviour. It also explains why you can sucessfully print out all the values when you are still inside the toNode function, at that point the nodes array has not been destroyed.
When you use pointers it's up to you to make sure that the object being pointed to is not destroyed before the pointer is. C++ does not do this for you. The usual way to ensure this is to use dynamic memory allocation (i.e. to use new). This is the normal technique for creating linked lists.
My instructions are...
// Description: Places the payload contents of the list in the
// array referenced by 'populateMeWithElements'.
// Returns the number of elements that were placed
// in the provided memory location.
// Precondition: Enough memory has been allocated to the provided
// memory location to hold the full contents
// of the list.
// Postcondition: The memory allocated for 'populateMeWithElements'
// has been deallocated after the completion of
// this method call.
int getListElements(int* populateMeWithElements);
I have this written...
int OOLList::getListElements(int* populateMeWithElements) {
int count = 0;
OOLNode* iterator = this->start;
int* populateMeWithElements = new int[getListSize()];
for (int i = 0; iterator->next != NULL; i++) {
populateMeWithElements[i] = iterator->payload;
iterator = iterator->next;
count++;
}
return count;
}
but I am not sure if it is correct and if it is...
How do I display the contents in my driver
Where I deallocate the memory (do I do this in the driver or in the function that I wrote?)
Thanks in advance for any suggestions or help.
Your function is declaring a local populateMeWithElements variable that shadows the input populateMeWithElements parameter. You are filling the array that you allocate locally, you are not populating the caller's array at all.
For that matter, your function should not be using new[] at all, since the instructions clearly state that the precondition of the function is that the caller has already allocated an array of sufficient size beforehand, and will deallocate that array after the function exits. So, your function's job is merely to fill the caller's array, nothing more.
And on that task, your loop is wrong. iterator->next != NULL needs to be iterator != NULL, otherwise if the list is empty then the 1st access of iterator->next will fail, and if the list is not empty then the payload of the tail node will be skipped.
Try this instead:
int OOLList::getListElements(int* populateMeWithElements) {
int count = 0;
OOLNode* iterator = this->start;
for (int i = 0; iterator != NULL; i++) {
populateMeWithElements[i] = iterator->payload;
iterator = iterator->next;
++count;
}
return count;
}
OOLList list;
// populate list as needed...
int* elements = new int[list.getListSize()];
int count = list.getListElements(elements);
for(int i = 0; i < count; ++i) {
// use elements[i] as needed...
}
delete[] elements;
The function can be simplified a bit further:
int OOLList::getListElements(int* populateMeWithElements) {
int count = 0;
for (OOLNode* iterator = this->start; iterator != NULL; iterator = iterator->next) {
populateMeWithElements[count++] = iterator->payload;
}
return count;
}
Or even:
int OOLList::getListElements(int* populateMeWithElements) {
int* ptr = populateMeWithElements;
for (OOLNode* iterator = this->start; iterator != NULL; iterator = iterator->next) {
*ptr++ = iterator->payload;
}
return ptr - populateMeWithElements;
}
Having a structure
struct Person{
Person( int i):id(i){};
Person * next;
int id;
};
class Test{
public:
void addList( Person *&f , Person *&l , int i){
Person *tmp = new Person(i);
if( f == nullptr ){
f = tmp;
l = tmp;
return;
}
first -> next = tmp;
last = tmp;
}
void addArr( int *arr , int i ){
arr[index++] = i;
}
void print( ){
for( int i = 0; i < index; i ++)
cout << arr[i] << " ";
cout << endl;
}
Person *first = nullptr;
Person *last = nullptr;
int index = 0;
int *arr = new int[10];
};
function addList add node into linked list and addArr adds element into arr.
My question is about pointer and reference pointer.
in
void addList( Person *&f , Person *&l , int i){
Person *tmp = new Person(i);
if( f == nullptr ){
f = tmp;
l = tmp;
return;
}
first -> next = tmp;
last = tmp;
}
I need to pass pointer as reference. Otherwise , the local copy of pointer would be changed not outer. I assume compilator creates something like
Person *temporary = new Person(*f);
But would I not have to pass array by reference?
I am quite confused by this fact.
But would i do not have to pass array by reference?
Not in this case, by passing your Person pointer by reference in the addList function, you are able to alter the pointer itself. That is like saying, "Pointer, use a different address". This is possible, as it was passed by reference.
Whereas in your addArr function, you are not altering the pointer to the array itself. Rather, you are altering the data that is pointed to. "Pointed to data, use a different value". This data arr is pointing to is the same data outside the scope of the function.
So, no, you don't have to pass the array by reference.
#include<iostream>
using namespace std;
struct data {
int x;
data *ptr;
};
int main() {
int i = 0;
while( i >=3 ) {
data *pointer = new data; // pointer points to the address of data
pointer->ptr = pointer; // ptr contains the address of pointer
i++;
}
system("pause");
}
Let us assume after iterating 3 times :
ptr had address = 100 after first loop
ptr had address = 200 after second loop
ptr had address = 300 after third loop
Now the questions are :
Do all the three addresses that were being assigned to ptr exist in the memory after the program gets out of the loop ?
If yes , what is the method to access these addresses after i get out of the loop ?
Well the memory is reserved but you have no pointer to the memory so that's whats called a memory leak (reserved memory but no way to get to it). You may want to have an array of data* to save these pointers so you can delete them when you are done with them or use them later.
For starters, there will be no memory allocated for any ptr with the code you have.
int i = 0;
while( i >= 3)
This will not enter the while loop at all.
However, if you are looking to access the ptr contained inside the struct then you can try this. I am not sure what you are trying to achieve by assigning the ptr with its own struct object address. The program below will print the value of x and the address assigned to ptr.
#include<iostream>
using namespace std;
struct data {
int x;
data *ptr;
};
int main() {
int i = 0;
data pointer[4];
while( i <=3 ) {
pointer[i].x = i;
pointer[i].ptr = &pointer[i];
i++;
}
for( int i = 0; i <= 3; i++ )
{
cout<< pointer[i].x << endl;
cout<< pointer[i].ptr << endl;
}
}
OUTPUT:
0
0xbf834e98
1
0xbf834ea0
2
0xbf834ea8
3
0xbf834eb0
Personally, when I know the number of iterations I want to do, I choose for loops and I use while only when I am looking to iterate unknown number of times before a logical expression is satisfied.
I cannot guess what you are trying to achieve...
But Me thinks, you are trying to achieve similar to this....
But, If you want to make linked list using your implementation, you can try this...
#include<iostream.h>
struct data {
int x;
data *ptr;
data()
{
x = -1;
ptr = NULL;
}
};
data *head = new data();
data *pointer = head;
int main() {
int i = 0;
while( i <=3 ) {
data *pointer = new data();
pointer->x = /*YOUR DATA*/;
::pointer->ptr = pointer;
::pointer = pointer;
i++;
}
i=0;
data* pointer = head->next;
while( i <=3 ) {
cout<<pointer->x;
pointer = pointer->ptr;
i++;
}
system("pause");
}
This will print , the elements in the linked list;
void longcatislong(int* cat, int &size, int &looong)
{
int* longcat = new int[looong*2];
for(int i = 0; i < size; i = i + 1)
longcat[i] = cat[i];
delete [] cat;
cat = longcat;
looong = looong * 2;
}
Soup guys. I'm /r/equesting some help with this problem I have with my code. Apparently something in my C++ code caused a heap corruption error and that something is delete[] cat. cat is a dynamic array of ints that was created with the new operator and a pointer. Why, then is it that when I use the array delete the whole program decides to get crushed under a steamroller and say I got heap corruption. I'm 12 and what is this?
You are passing cat pointer by value so whatever changes you do inside the function is not reflected outside. You need to pass the pointer by reference like int*& cat.
cat is not being returned to the caller of this function. You're only changing the local copy when you execute cat = longcat.
That means the parameter that you passed in to this function still points to the old address which you've very inconveniently deleted.
Either pass it in as a reference or do the old C double pointer trick and pass in its address.
You may also want to ensure that the first time you call this, cat has a valid value and that size and looong are compatible (looong * 2 >= size) lest you corrupt memory.
Have a look at the following code which illustrates your problem:
#include <iostream>
void longcatislong1(int* cat, int &size, int &looong)
{
int* longcat = new int[looong*2];
for(int i = 0; i < size; i = i + 1)
longcat[i] = cat[i];
delete [] cat;
cat = longcat;
looong = looong * 2;
}
void longcatislong2(int*& cat, int &size, int &looong)
{
int* longcat = new int[looong*2];
for(int i = 0; i < size; i = i + 1)
longcat[i] = cat[i];
delete [] cat;
cat = longcat;
looong = looong * 2;
}
int main (void) {
int sz = 0;
int lng = 10;
int *ct = 0;
std::cout << ct << std::endl;
longcatislong1 (ct, sz, lng);
std::cout << ct << std::endl;
longcatislong2 (ct, sz, lng);
std::cout << ct << std::endl;
return 0;
}
Its output is:
0
0
0x9c83060
meaning that the longcatislong1 call did not successfully set ct on return. The longcatislong2 function, which passes the pointer in as a reference, does set ct correctly.
So let's say you have a valid pointer to 0xf0000000. When you call your original function, a new memory block is allocated, the data is copied across and the old block is deleted.
But the ct variable still points to the old block.
The next time you call the function, or even if you dereference ct elsewhere, you're in for a world of pain, commonly called undefined behaviour.
By making the first parameter a reference type, changes made in the function are reflected back in the variable that was passed in.
You should remove int* cat by int** cat in function args, and then replace all cat insertions
in function body by *cat even in cat[i] placement.
void longcatislong(int** cat, int &size, int &looong)
{
int* longcat = new int[looong*2];
for(int i = 0; i < size; i = i + 1)
longcat[i] = *cat[i];
delete [] *cat;
*cat = longcat;
looong = looong * 2;
}
And then when you call function call it like that:
longcatislong(&cat, size, looong);