Array of pointers unexplained behavior (with shallow copy) c++ - c++

This is a behavior with array of pointers in C++ (GNU GCC compiler) that I can't find an explanation for, hopefully someone can clear the confusion.
I'm creating an array of pointers (arr_ptr), The pointers are pointing to valid data, then I create another array of pointers (arrcopy), I do -what I think- a shallow copy (arrcopy = arr_ptr), and I get the data as expected...so far so good.
The part I am not understanding is, after I delete arr_ptr, shouldn't arrcopy still be pointing to my valid data? Why this is not happening?
Thank you.
int main()
{
int a = 1; int b=2; int c=3;
int* ap = &a; int* bp = &b; int* cp = &c;
// Array of pointers
int** arr_ptr = new int*[3];
arr_ptr[0] = ap;
arr_ptr[1] = bp;
arr_ptr[2] = cp;
//shallow copy
int** arrcopy = arr_ptr;
cout << "Values: " << *(arr_ptr[0]) << " " << *(arrcopy[0]) << endl;
cout << "Addresses: " << arr_ptr[0] << " " << arrcopy[0] << endl;
cout << endl;
a++;
cout << "After Incrementing a:" << endl;
cout << *(arr_ptr[0]) << " " << *(arrcopy[0]) << endl;
cout << arr_ptr[0] << " " << arrcopy[0] << endl;
cout << endl;
*(arr_ptr[0]) = 5;
cout << "After updating a value to 5:" << endl;
cout << *(arr_ptr[0]) << " " << *(arrcopy[0]) << endl;
cout << arr_ptr[0] << " " << arrcopy[0] << endl;
cout << endl;
//so far so good - works as expected
//deleting arr_ptr
delete[] arr_ptr;
// Why?: shouldn't arrcopy still be pointing to A
cout << "Values: " << *(arr_ptr[0]) << " " << *(arrcopy[0]) << endl; //The result I am expecting here is: unknown_value 5
cout << "Addresses: " << arr_ptr[0] << " " << arrcopy[0];
cout << endl;
return 0;
}
After Shallow copy of arr_ptr to arrcopy (arrcopy = arr_ptr)
After deleting arr_ptr, shouldn't arrcopy still be pointing to the memory locations of a,b,c data values?!

You do not have 2 arrays after the assignment you have 2 pointers to the same array:
arr_ptr -------->|ptr0|ptr1|ptr2|ptr3... (allocated memory)
arr_cpy = arr_ptr; // copy only the pointer
Now both pointers point to the same allocated memory:
arr_ptr -------->|ptr0|ptr1|ptr2|ptr3...
^
arr_cpy -----------|
delete[] arr_ptr; // invalidate the memory that both pointers point to
This gives:
arr_ptr -------->|xxx0|xxx1|xxx2|xxx3... (invalid memory)
^
arr_cpy -----------|
It doesn't matter which pointer you call delete[] on, they both point to the same block of allocated memory so after the call they both point to the same block of invalidated memory.
What you need to do is copy the whole array:
int** arr_cpy = new int*[3];
std::copy(arr_ptr, arr_ptr + 3, arr_cpy); // copy the pointers to the new array
Or much better use a std::vector:
int main()
{
int a = 1; int b=2; int c=3;
int* ap = &a; int* bp = &b; int* cp = &c;
// Array of pointers
std::vector<int*> arr_ptr{3};
arr_ptr[0] = ap;
arr_ptr[1] = bp;
arr_ptr[2] = cp;
//shallow copy
std::vector<int*> arr_cpy = arr_ptr; // copy the whole vector
// ... etc.
No need for delete[], memory is deallocated automatically when your vector goes out of scope.

Neither arr_ptr nor arrcopy is an array. Both are just pointers.
A C++ pointer is a little overloaded. It can point to a single object or to an array of objects.
In your case arr_ptr is initialized with the address of the array that you allocated in the dynamic memory. But it could as well be initialized with an address of a single object on the stack:
int i = 0;
int** arr_ptr = &i;
By copying the value of that pointer into another pointer of the same type you simply have two pointers referring to the same memory location:
// array in the heap
[xxxxxxxxxxxxxxxxxxxxxxx]
^
/ \
/ \
/ \
arr_ptr arrcopy

Related

How do pointers to C-Strings work compare to pointers to arrays of different data types?

I am a student in a CompSci intro class and I have a very basic understanding of pointers in C++. I had noticed in attempting to complete an assignment that a character array / c-string uses pointers differently than other data types.
For example, please consider the following code I created:
#include <iostream>
using std::cout, std::endl;
int main()
{
int inta[] = {1,2,3};
int* p1 = inta;
cout << "p1 = " << p1 << endl;
cout << "*p1 = " << *p1 << endl;
cout << "sizeof(p1) = " << sizeof(p1) <<
", sizeof(*p1) = " << sizeof(*p1) << endl;
char stra[] = "Dog";
char* p2 = stra;
cout << "p2 = " << p2 << endl;
cout << "*p2 = " << *p2 << endl;
cout << "sizeof(p2) = " << sizeof(p2) <<
", sizeof(*p2) = " << sizeof(*p2) << endl;
return 0;
}
The output of *p1 and *p2 are both the first value of the array. However, while the output of p1 is the pointer to the first element of inta (which tracks from online research), the output of p2 is the entire word "Dog". The sizes of p1 and p2 are the same, the size of *p1 and *p2 are 4 and 1 respectively. Is there something I am missing?
I am using Visual Studio Community 2022 and created a normal project.
Thank you, and I appreciate your help!
p1 is a pointer to an int. *p1 is thus an int -> size = 4
p2 is a pointer to a char, *p2 is thus a char -> size = 1
passing a char pointer to an output operator (cout<< or printf etc) will keep reading chars until it reaches the null char at the end of the string

New allocates the same memory with each iteration

I am trying to build a list-like heap storage of C-strings.
This is a simplified part of the program.
However, with each iteration new brings up the same address.
#include <iostream>
class listStringContainer {
public:
listStringContainer(const char* c);//constructor
};
int main(){
listStringContainer lsc1 ("Lorem ipsum");// calling the constructor
}
listStringContainer::listStringContainer(const char* c) {//constructor
char * Memory_Address;
auto time{5};
while (--time>=0) {
Memory_Address = new char[16];
//the memory location is to be saved into a vector
std::cout << "Memory_Address: "<< &Memory_Address << std::endl;
}
}
Output:
Memory_Address: 0x62fed8
Memory_Address: 0x62fed8
Memory_Address: 0x62fed8
Memory_Address: 0x62fed8
Memory_Address: 0x62fed8
The same result on g++ and MSVS.
Why does new appoint the same location and how to make new appoint different addresses?
You need to cast the static_cast<void*>(Memory_Address) to get the value stored in Memory_Address.
Lets consider:
char * p;
p= new char[16];
strcpy(p, "Hello");
cout << *p << endl; // Prints 'H'
cout << &p << endl; // Prints address of p
cout << p << endl; // Prints "Hello"
cout << static_cast<void*>(p) << endl; // Prints address of p[0]
Consider the below same scenario but with Integer data type:
int * ptr;
ptr= new int[16];
ptr[0] = 10;
cout << *ptr << endl; // Prints 10
cout << &ptr << endl; // Prints ptr address
cout << ptr << endl; // Prints address of ptr[0]
Therefore, Integer doesn't require casting to void* for getting &ptr[0]

C++ returning pointer

#include <iostream>
using namespace std;
int* createArray();
int main() {
int *arr = createArray();
cout << "Main: " << arr << endl;
arr[0] = 0;
arr[1] = 1;
cout << arr[0] << endl;
cout << arr[1] << endl;
}
int* createArray() {
int arr[2];
cout << "createArray()1: " << arr << endl;
return arr;
}
I don't understand why if I only call this statement
cout << arr[0] << endl;
or
cout << arr[1] << endl;
can show a correct value.
But if I call both statement, it will show
createArray()1: 00AFFAF4
Main: 00AFFAF4
0
11533068 //Don't show 1
The pointer returned by createArray points to a non-existing object. The local array was destroyed when the function returned. When you dereference the dangling pointer in main, the behaviour of your program is undefined.
Solution: Don't ever return pointers or references to local variables. In this case, you could for example return a copy of the array. You cannot return a raw array by value, but you can return a wrapper structure like std::array<int, 2>.
You are returning a pointer to local variable which is destroyed at function block end.
But beware, when you use new operator, you need to use delete somewhere else (atleast without of use of smart pointers).
Solution:
int* createArray() {
int* arr = new int[2];
// your code
return arr;
}

Why does this work? Assigning a new int value to a pointer after delete

I have this code:
#include <iostream>
using namespace std;
void main(){
int *ptr = new int(15);
cout << "Address of ptr: " << ptr << endl;
cout << "Content of ptr: " << *ptr << endl << endl;
delete ptr;
*ptr = 30;
cout << "Address of ptr: " << ptr << endl;
cout << "Content of ptr: " << *ptr << endl;
}
And this is the output:
Address of ptr: 007B81F0
Content of ptr: 15
Address of ptr: 007B81F0
Content of ptr: 30
Why does this work? Why can I still use the pointer? What happened?
Is this useful at some point?
This is typical undefined behaviour, and it happens to work because the memory that was previously allocated for *ptr was not yet reclaimed by the operating system*. You should never rely on this kind of code.
Note that the address of the pointer stays the same, as C++ runtime doesn't bother to nullify the pointer (it takes time and in C++ you don't pay for what you don't need).
*see also Remy Lebeau's comment below.
Just as vsoftco explained, the memory was not yet reclaimed by the OS. Now if we do something like below, you may experience a completely different output:
int *ptr = new int(15);
cout << "Address of ptr: " << ptr << endl;
cout << "Content of ptr: " << *ptr << endl << endl;
delete ptr;
int *ptr2 = new int(15); // making a new ptr to use memory.
*ptr = 30; // <- OPS!
delete ptr2; // deleting new ptr.
cout << "Address of ptr: " << ptr << endl;
cout << "Content of ptr: " << *ptr << endl;
Anyway, the result is undefined behaviour
Although delete key word was used but for some reason de-allocation of pointer did not happen.(Probably scope of the pointer was still in use)
Using pointers is an important aspect of C++.It is important to use pointers to deal at reference level or with memory addresses.

Copy contents of one array to another with pointers

I've been learning C++ on my own for a while now, and I've come to a "roadblock" when it comes to pointers. I'm using this as my http://www.cplusplus.com/doc/tutorial/pointers/ learning material, but I'm still having an issue. So to test something I wanted to copy the contents of one array into another. I wrote the following.
char arrayA[15] = "abcdef";
char arrayB[15];
char *a = arrayA;
char *b = arrayB;
cout << "before loop:" << endl;
cout << a << endl;
cout << b << endl;
while (*a != '\0') {
// Copy the contents of a into b
*b = *a;
// Step
a++;
b++;
}
// Assign null to the end of arrayB
*b = '\0';
cout << "after loop:" << endl;
cout << a << endl;
cout << b << endl;
I get the following results.
before loop:
abcdef
after loop:
When I cout the contents before the loop I get the expected results. a contains "abcdef" and b is nothing, because there is no value yet. Now after the loop, both a and b show no results. This is where I am lost. I used * to dereference both a and b and assign the value of a into b. Where did I go wrong? Do I need to use the & with this?
Solution:
After the loop is complete, pointer *a is pointing to the end of arrayA and pointer *b is pointing to the end of arrayB. So to get the full results of arrayB simply cout << arrayB. Or create a pointer that never changes and always points to arrayB char *c = arrayB and cout << c at the end of the loop.
After the loop a and b have changed, they then point to the end of the string. You need to make a copy of the pointers to step through so that as you iterate you're not changing the location of a and b.
The problem is that you're outputting your temporary variables that were used to iterate through the array. They are now at the end of the copied data. You should output the value of arrayA and arrayB instead.
Remember begin of array. In this moment you are incrementing pointers and printing something which is pointed by them at the end of arrays, after loop ends.
char arrayA[15] = "abcdef";
char arrayB[15];
char *a_beg = arrayA;
char *b_beg = arrayB;
char *a;
char *b;
cout << "before loop:" << endl;
cout << a_beg << endl;
cout << b_beg << endl;
a = a_beg;
b = b_beg;
while (*a != '\0') {
// copy contents of a into b and increment
*b++ = *a++;
}
// assign null to the end of arrayB
*b = '\0';
cout << "after loop:" << endl;
cout << a_beg << endl;
cout << b_beg << endl;