How to I Properly Dereference a pointer to a pointer array? - c++

I'm working on an assignment involving string pointers. There are two functions. The first takes in an array of strings, places the address of each element into a separate array, and then returns the pointer to that array. The second function takes the pointer that was returned and prints out the elements of the original array with just the pointer. But when I test it, the dereferenced string** ptrToPtr is different in each pointer. I want to know why
Here is Function 1:
string** arrayOfPtrs(string arr[], int size)
{
string* ptrArray; //The array of string pointers
string** ptrToPtr; //A pointer to the array of string pointers
ptrArray = new string[size];
//ptrArray = arr;
int i = 0;
while (i < size)
{
ptrArray = &arr[i];
i++;
ptrArray++;
}
ptrToPtr = &ptrArray;
return ptrToPtr;
}
Here is Function 2:
void outputArray(string** arr, int size)
{
int count = size; //An int variable that stores the size of array
string* ptr = *arr; //A pointer that stores the address to the last element
//in the string pointer array
while (count > 0)
{
cout << *(ptr - count) << " ";
count--;
}
cout << endl;
}
And here is part of main():
string strArr[] = { "echo", "charlie", "delta", "bravo", "delta" };
string** strPtrs;
strPtrs = arrayOfPtrs(strArr, 5);
cout << "Actual results: ";
outputArray(arrayOfPtrs(strArr, 5), 5);
cout << endl << endl;
I'm I going wrong anywhere? Or is there a better way to deference a pointer to a string pointer?
Here is a similar program ran completely in main:
int main()
{
string words[30];
string* s;
s = new string[30];
string** t;
createArray(30, words);
int num = 0;
t = &s;
while (num < 30)
{
s = &words[num];
num++;
s++;
}
string* u = *t;
int j = 30;
for (int i = 0; i < 30; i++)
{
cout << "*(s - " << j << ") - " << *(s - j) << endl;
cout << "words[ " << i << " ] - " << words[i] << endl;
cout << "*(u - " << j << " ) - " << *(u - j) << endl << endl;
j--;
}
}
And this program works perfectly. Any ideas?

This is incorrect:
while (i < size)
{
ptrArray = &arr[i];
i++;
ptrArray++;
}
Replace ptrArray = &arr[i]; with *ptrArray = arr[i];. As it stands now, you're just overwriting the same pointer each time through the loop and never doing anything useful with it.
This is also incorrect:
string* ptrArray; //The array of string pointers
// ...
ptrToPtr = &ptrArray;
return ptrToPtr;
As soon as you return that, it becomes dangling. You're not allowed to use pointers to local (stack) variables once they're out of scope.

Firstly, I see a few problems in your setup
You don't need this for practical reasons. If you want to have the address of each element in the array, you can calculate it by incrementing the pointer to the first element (which is the array in fact). If you only do that for educational reasons forget about this
string* ptrArray = new ... Now you have an array of strings (array is semantically equaivalent to pointer to first element). But you want an array of string pointers. So you need string** ptrArray = new ... and this cascades to the rest of the function being incorrect.
You never delete the array allocated with new. This results in the memory not being free'd. You need to delete[] *strPtrs;in your last code snippet to free the memory you allocated in your method. In general it is a good idea to let the one who allocates the memory be responsibly for freeing it. I show you another idea below to handle this.
After your copy operations the pointer points past your array. Then you return a pointer to it. You applied the correct arithmetics when outputting the values in you second snippet, but I would never want to have such a pointer going around. Conventionally it should point to the first element. At least when deleting the array-pointer it has to point to the first element, otherwise you get undefined behavior e.g. deleting another array.
Lastly:
string* ptrArray; //The array of string pointers
string** ptrToPtr; //A pointer to the array of string pointers
ptrToPtr points to ptrArray, which is a local variable. It becomes invalid when leaving the function and thus it will be undefined behavior to dereference the returned pointer.
There is a common approach used by some standard libraries (e.g. snprintf from cstdio), so the caller is responsible for allocation and deallocation:
void arrayOfPtrs(string arr[], int size,/*new param*/ string** outArray)
{
string** iter = outArray; // Iterator pointer
int i = 0;
while (i < size)
{
*iter = &arr[i];
i++;
iter++;
}
}
What happens here, is that the caller gives the function a pointer to the pointers (it points to the first pointer). Note that a pointer can be used as an array with index operators etc. So it is in fact an array of pointers. You then fill it by incrementing the copied pointer so it jumps from pointer element to pointer element. Where the array actually is stored is not the problem of this function.
Use it like this:
// Variant 1: Use local variable if size is constant
string* arr[5];
arrayOfPtrs(strArr, 5, arr);
std::cout << *arr[0]; // Dereferences a pointer in arr to get the string which is actually in strArr
// Variant 2: Allocate heap memory (if you need dynamic size)
int size ...; // From somewhere
string** arr = new string[size];
arrayOfPtrs(strArr, size, arr);
std::cout << *arr[0]; // Same again
... // Do further work
delete[] arr; // Free memory
So you have to allocate memory (or use a local variable) before you call the function and then pass it to the function. In the double pointer, the first * is meant for the data type which is "pointer to string" and the second designates it as a "pointer-array".

Related

Understanding C++ syntax for array pointers double brackets

I writing a script which receives a structure (Evt) which holds an array of pointers uint16_t *DataChannel[Evt->ChSize[ch]]. I can then loop over the data and print out of the value as so:
for(uint16_t ch=0; ch<sizeof(Evt->DataChannel)/sizeof(Evt->DataChannel[0]); ++ch){
for(int adc_it=0; adc_it<(Evt->ChSize[ch]); ++adc_it){ for(int adc_it=0; adc_it<(Evt->ChSize[ch]); ++adc_it){
std::cout << (Evt->DataChannel)[ch][adc_it] << " ";
}
}
I don't understand where the second bracket comes from ([adc_it]), what it is doing and how it works. I think (Evt->DataChannel) should be a uint16_t which is not an array so why the second bracket?
I tried to replicate this in a quick piece of code:
#include <iostream>
int main()
{
int* test[10];
*(test[0]) = 5;
std::cout << test[0][0] << std::endl; //Gives 5
std::cout << test[0][1] << std::endl; //Seg faults
return 0;
}
Again can some explain what test[0][0] is doing because I have no idea and why it runs but test[0][1] fails?
Cheers
Dom
C++ permits the use of array notation [ ] when dereferencing a pointer.
In the simplest case, you can say something like:
char *ptr = new char[10];
That creates a pointer to char, which points to the first character in an array of ten characters allocated with new[].
After doing this:
char ch = *ptr;
and
char ch = ptr[0];
do the exact same thing. However since there are 10 characters in the array, you can also say:
char ch5 = ptr[5];
to access an element further up the array.
This leads to an eqivalence in C++ (and C, where it originated) that:
ptr[x];
is identical to:
*(ptr + x);
for any pointer / array referenced by ptr and any index x.
Already the first part
int* test[10];
*(test[0]) = 5;
is undefined behaviour, because you dereference an uninitialized pointer value.
Explanation:
int* test[10] is an array of 10 pointers to ints, each pointer
being not initialized and definitely not pointing to a memory that
you reserved to store ints.
test[0] gives an uninitialized pointer; already this statement is
UB
*(test[0]) = 5 dereferences an arbitrary pointer value, definitely
UB
Try:
int* test[10];
int firstLine[5] = { 1,2,3,4,5 };
test[0] = firstLine;
cout << test[0][0]; // gives 1, firstLine[0]
cout << test[0][1]; // gives 2, is the same as firstLine[1]
// cout << test[1][0]; // undefined behaviour, the second pointer in array test is not initialized

What is the type of a pointer to a 2D array?

I know that the following is not correct:
int arr[2][3] = {}; //some array initialization here
int** ptr;
ptr = arr;
But I am quite surprised that the following lines actually work
int arr[2][3] = {}; //some array initialization here
auto ptr = arr;
int another_arr[2][3] = {}; //some array initialization here
ptr = another_arr;
Can anyone possibly explain what is the type assigned to ptr in the second block of code, and what happened underneath?
Well, arrays decay to pointers when used practically everywhere. So naturally there's decay going on in your code snippet too.
But it's only the "outer-most" array dimension that decays to a pointer. Since arrays are row-major, you end up with int (*)[3] as the pointer type, which is a pointer to a one-dimensional array, not a two dimensional array. It points to the first "row".
If you want ptr's deduction to be a pointer to the array instead, then use the address-of operator:
auto ptr = &arr;
Now ptr is int(*)[2][3].
In
auto ptr = arr;
arr decays into a pointer to its first element in the normal way; it's equivalent to
auto ptr = &arr[0];
Since arr[0] is an array of three ints, that makes ptr a int (*)[3] - a pointer to int[3].
another_arr decays in exactly the same way, so in
ptr = another_arr;
both sides of the assignment have the type int (*)[3], and you can assign a T* to a T* for any type T.
A pointer to arr itself has type int(*)[2][3].
If you want a pointer to the array rather than a pointer to the array's first element, you need to use &:
auto ptr = &arr;
First, let's look at why you can't assign int arr[2][3] to int **. To make it easier to visualise, we'll initialise your array with a sequence, and consider what it looks like in memory:
int arr[2][3] = {{1,2,3},{4,5,6}};
In memory, the array data is stored as a single block, just like a regular, 1D array:
arr: [ 1, 2, 3, 4, 5, 6 ]
The variable arr contains the address of the start of this block, and from its type (int[2][3]) the compiler knows to interpret an index like arr[1][0] as meaning "take the value that is at position (1*2 + 0) in the array".
However for a pointer-to-pointer (int**), it is expected that the pointer-to-pointer contains either a single memory address or an array of memory addresses, and this/these adress(es) point to (an)other single int value or array of ints. Let's say we copied the array arr into int **ptrptr. In memory, it would look like this:
ptrptr: [0x203F0B20, 0x203F17D4]
0x203F0B20: [ 1, 2, 3 ]
0x203F17D4: [ 4, 5, 6 ]
So in addition to the actual int data, an extra pointer must be stored for each row of the array. Rather than converting the two indexes into a single array lookup, access must be performed by making a first array lookup ("take the second value in ptrptr to get an int*"), then nother array lookup ("take the first value in the array at the address held by the previously obtained int*").
Here's a program that illustrates this:
#include <iostream>
int main()
{
int arr[2][3] = {{1,2,3},{4,5,6}};
std::cout << "Memory addresses for int arr[2][3]:" << std::endl;
for (int i=0; i<2; i++)
{
for (int j=0; j<3; j++)
{
std::cout << reinterpret_cast<void*>(&arr[i][j]) << ": " << arr[i][j] << std::endl;
}
}
std::cout << std::endl << "Memory addresses for int **ptrptr:" << std::endl;
int **ptrptr = new int*[2];
for (int i=0; i<2; i++)
{
ptrptr[i] = new int[3];
for (int j=0; j<3; j++)
{
ptrptr[i][j] = arr[i][j];
std::cout << reinterpret_cast<void*>(&ptrptr[i][j]) << ": " << ptrptr[i][j] << std::endl;
}
}
// Cleanup
for (int i=0; i<2; i++)
{
delete[] ptrptr[i];
ptrptr[i] = nullptr;
}
delete[] ptrptr;
ptrptr = nullptr;
return 0;
}
Output:
Memory addresses for int arr[2][3]:
0x7ecd3ccc0260: 1
0x7ecd3ccc0264: 2
0x7ecd3ccc0268: 3
0x7ecd3ccc026c: 4
0x7ecd3ccc0270: 5
0x7ecd3ccc0274: 6
Memory addresses for int **ptrptr:
0x38a1a70: 1
0x38a1a74: 2
0x38a1a78: 3
0x38a1a90: 4
0x38a1a94: 5
0x38a1a98: 6
Notice how the memory addresses always increase by 4 bytes for arr, but for ptrptr there is a jump of 24 bytes between values 3 and 4.
A simple assignment can't create the pointer-to-pointer structure needed for type int **, which is why the loops were necessary in the above program. The best it can do is to decay the int[2][3] type into a pointer to a row of that array, i.e. int (*)[3]. That's what your auto ptr = arr; ends up as.
What is the type of [...]
Did you already try to ask the compiler to tell you the type of an expression?
int main()
{
int arr[2][3] = {{0,1,2}, {3,4,5}}; // <-- direct complete initialized here
auto ptr = arr; // <-- address assignment only
cout << "arr: " << typeid(arr).name() << endl;
cout << "ptr: " << typeid(ptr).name() << endl;
return 0;
}
I've to confess that the output
arr: A2_A3_i
ptr: PA3_i
seems to be not very readable at first glance (compared to some other languages), but when in doubt it may help. It's very compact, but one may get used to it soon. The encoding is compiler-dependent, in case you are using gcc, you may read Chapter 29. Demangling to understand how.
Edit:
some experimentation with some simple_cpp_name function like this rudimentary hack
#include <typeinfo>
#include <cxxabi.h>
#include <stdlib.h>
#include <string>
std::string simple_cpp_name(const std::type_info& ti)
{
/// simplified code extracted from "Chapter 29. Demangling"
/// https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
char* realname = abi::__cxa_demangle(ti.name(), 0, 0, 0);
std::string name = realname;
free(realname);
return name;
}
will show you that auto &rfa = arr; makes rfa having the same type as arr which is int [2][3].

After passing by reference to modify an array, why it stays the same?

I am practicing pointers by creating a Big Number struct, which has numDigits (number of digits) and digits (contents of the big number).
I create a function called removeZero(). After passing the integer array and the size n into it, because of passing by reference, I am supposed to cut down the leading zeros for my input. It works, when the integer array is in main function. However, when I pass an array that is in readDigits, it does not return with a non-leading-zero version. Why? How to fix it?
struct BigNum{
int numDigits;
int *digits; //the content of the big num
};
int main(){
int A[] = {0,0,0,0,0,0,1,2,3};
int n=9;
int *B=A;
//removeZero(A,n); If I use this, it cannot compile
//error: invalid initialization of non-const reference of type ‘int*&’ from an rvalue of type ‘int*’
removeZero(B,n);
for (int i=0; i<n; i++){
std::cout << *(B+i) << std::endl;
}
BigNum *num = readDigits();
return 0;
}
BigNum* readDigits(){
std::string digits;
std::cout << "Input a big number:" << std::endl;
std::cin >> digits;
//resultPt in heap or in stack?
int *resultPt = new int[digits.length()]; //in heap
int n = digits.length();
toInt(digits,resultPt);
removeZero(resultPt,n);
//Output the leading zeros, why?
for (int i=0; i<n; i++){
std::cout << *(resultPt +i) << std::endl;
}
BigNum *numPtr = new BigNum();
numPtr->numDigits = n;
numPtr->digits = resultPt;
return numPtr;
}
void toInt(std::string& str, int *result){
for (int i=0;i<str.length() ;i++ ){
result[str.length()-i-1] = (int)(str[i]-'0');
}
}
void removeZero(int* &A,int& n){
int i=0;
while (A[i]==0){
i++;
}
A=A+i; //memory leak?
n=n-i;
}
bool areDigits(std::string num){
for(int i=0;i<num.length();i++){
if(num[i]<'0' || num[i] >'9'){
return false;
}
}
return true;
}
Note that an array and a pointer are two different things. When you pass an array to a function, it degrades to a const pointer. This means that you cannot pass an array to a function which expects a int*&.
It could be the problem of scope of numPtr.numPtr is local variable of function readDigits(). Instead of returning pointer. Pass num to readDigits().
The signature of your removeZero function is:
void removeZero(int* &A,int& n);
That means the forst parameter is a reference of a pointer but the pointer is a non-const one, and you cannot therefore pass an array there, as array is a constant pointer (starting address cannot be changed).
In fact you are changing the starting address within removeZero.
With removeZero, the while loop shopuld be changed from:
while (A[i]==0){
to:
while ((A[i]==0) && (i<n)){
You have a logic error in toInt.
void toInt(std::string& str, int *result){
for (int i=0;i<str.length() ;i++ ){
// This stores the digits in the reverse order.
result[str.length()-i-1] = (int)(str[i]-'0');
}
}
That line should be
result[i] = (int)(str[i]-'0');
If you intend to keep the digits in reverse order, then removeZero has to be changed keeping that in mind.
`
When you say
int *B=A;
you are just creating a pointer to point to the same memory
of the Array A. Just by incrementing the pointer(*B) within the function
removeZero
A=A+i;
you are not deleting anything but you are just incrementing the pointer(*B)
to point to subsequent memory location within the array.
The original array memory pointed to by A remains the same, since you
have not changed any contents of the array, but you have just
incremented a pointer pointing to the same memory location as that of the array.
Also there are so many problems, like "Debasish Jana" mentioned,
you have to change your while loop. ""Code-Apprentice" gave you the reason for your
compilation error when you uncomment your commented code.
Also within "removeZero" you are incrementing A by i instead of "1" like
A=A+1;
This is one of the reason for the strange behavior you experience
Even after changing all this, you cannot see your array getting changed,
since you are not modifying any of the contents of your array.
If you really want to delete the contents of the array and change it dynamically,
you have to go for Vector<>. With static memory allocation you cannot cut the
array size short by removing some elements here and there. Learn Vector<>!

C++: The value that the pointer is pointing to changes

I am trying to code a class that represents a set of integers. It's a homework assignment but for the life of me I cannot figure out this issue.
In the class "IntSet", I have two private variables; one is a pointer to an array the other is the size of the array. I can create objects of this class and they work as intended. But I have this function named "join" that returns an object of the IntSet class. It essentially concatenates the arrays together then uses that array to create the returning object.
Here is my code:
#include <iostream>
using namespace std;
class IntSet {
int * arrPtr;
int arrSize;
public:
//Default Constructor
IntSet() {
int arr[0];
arrPtr = arr;
arrSize = 0;
}
//Overloaded Constructor
IntSet(int arr[], int size) {
arrPtr = arr;
arrSize = size;
}
//Copy Constructor
IntSet(const IntSet &i) {
arrPtr = i.arrPtr;
arrSize = i.arrSize;
}
/*
* Returns a pointer to the first
* element in the array
*/
int* getArr() {
return arrPtr;
}
int getSize() {
return arrSize;
}
IntSet join(IntSet &setAdd) {
//Make a new array
int temp[arrSize + setAdd.getSize()];
//Add the the values from the current instance's array pointer
//to the beginning of the temp array
for (int i = 0; i < arrSize; i++) {
temp[i] = *(arrPtr + i);
}
//Add the values from the passed in object's array pointer
//to the temp array but after the previously added values
for (int i = 0; i < setAdd.getSize(); i++) {
temp[i + arrSize] = *(setAdd.getArr() + i);
}
//Create a new instance that takes the temp array pointer and the
//size of the temp array
IntSet i(temp, arrSize + setAdd.getSize());
//Showing that the instance before it passes works as expected
cout << "In join function:" << endl;
for (int j = 0; j < i.getSize(); j++) {
cout << *(i.getArr() + j) << endl;
}
//Return the object
return i;
}
};
int main() {
//Make two arrays
int arr1[2] = {2 ,4};
int arr2[3] = {5, 2, 7};
//Make two objects normally
IntSet i(arr1, 2);
IntSet j(arr2, 3);
//This object has an "array" that has arr1 and arr2 concatenated, essentially
//I use the copy constructor here but the issue still occurs if I instead use
//Inset k = i.join(j);
IntSet k(i.join(j));
//Shows the error. It is not the same values as it was before it was returned
cout << "In main function:" << endl;
for (int l = 0; l < k.getSize(); l++) {
cout << *(k.getArr() + l) << endl;
}
return 0;
}
The program compiles and the output as of now is:
In join function:
2
4
5
2
7
In main function:
10
0
-2020743083
32737
-2017308032
I don't know why but the 10 and 0 are always the same every time I recompile and run. Also, if I print out the address of the pointer rather than the value(in both the join function and the main function), I get the same memory address.
Sorry if I misuse terms, I come from a java background, so pointers and such are a little new to me. If any clarification is needed, please ask.
Thanks in advance.
int temp[arrSize + setAdd.getSize()];
This is a local array, its lifetime ends once the function returned.
IntSet i(temp, arrSize + setAdd.getSize());
Here you are constructing an IntSet with this array. In fact the constructor simply changes a member pointer to the value of temp:
IntSet(int arr[], int size) {
arrPtr = arr;
arrSize = size;
}
As a result, since the lifetime of the object that temp and consequently also i.arrPtr is pointing to ends after leaving join, you will have a wild pointer. Dereferencing this pointer later in main invokes undefined behavior.
You need to allocate the array dynamically with new[] and delete it later with delete[]. The same goes for your constructors. Also note that if you use new[] in join and delete[] in the destructor, then you also have to make sure that the copy constructor actually copies the array (create new array with new[] and copy contents). If you simply assign the pointer then both the source and destination object will point to the same array and they will also both try to delete it at deconstruction, again invoking undefined behaviour.
But since this C++, you might as well use a std::vector which does all of this for you. (or std::set if you actually want a integer set)
The quickest fix with your code is to change
int temp[arrSize + setAdd.getSize()];
into this
int * temp = new int[arrSize + setAdd.getSize()];
The thing is that you allocated temp on the stack, so when join() returns that memory is releases. By allocating memory on the heap (as per the fix) the memory is not released when join() returns.
There are other things wrong with your code -- depending on the point of the assignment. I think most of these will be fixed when you consider the implications of having memory on the heap.

Having difficulty working with pointers

I having some issue when it comes to initializing pointers.
void findMM (int *PMM, int *theG)
{
// code I haven't written yet. It will essentially take two variables from //theG and store it in MM
}
int main()
{
int size;
int MM [2] = {1000, 0};
int *theG = NULL;
cout << "\nPlease insert size of array:" << endl;
cin >> size;
theG = new int [size];
findMM(&MM, &theG); //Get error with &MM
delete [] theG;
return 0;
}
The complier says that argument of type int (*)[2] is incompatible with parameter of type int ** So obviously that I have issue with the code in particular my (reference?) of array MM. Or perhaps there is other obvious faults that I am missing?
Edit attempt 2
void findMM (int *PMM, int *theG)
{
PMM [1] = 5;
theG [0] = 7;
}
int main()
{
int size;
int MM [2] = {1000, 0};
int *theG = NULL;
cout << "\nPlease insert size of array:" << endl;
cin >> size;
theG = new int [size];
findMM(MM, theG);
cout << MM [1] << endl << theG[0];
delete [] theG;
return 0;
}
The output would be 5 and 7 correct?
Since MM is an array, &MM is a pointer to an array (that's the type int (*)[2] that you see in the error). Instead, you seem to want to pass a pointer to the first element of the array. There are two ways to do that. Firstly, you can explicitly get the first element and then take the address of it: &MM[0]. Secondly, you can rely on array-to-pointer conversion to do it for you and just pass MM. Array-to-pointer conversion converts an array to a pointer to its first element.
I know this question has already been answered but I believe I can contribute to the asker's understanding.
Let's start with the basics:
void main()
{
int a = 2; // a is an int
cout << a << endl; // print 2
int *b; // b is a pointer-to-int
b = &a; // store the address of a in b
cout << *b << endl;// print the value that b points to, which is 2
int my_array = new int[3]; // allocate an array with 3 integers
my_array[0] = 50; // store 50 in the first element of the array
my_array[1] = 51; // store 51 in the second element of the array
my_array[2] = 52; // store 52 in the third element of the array
cout << c[0] << endl; // print 50
some_function(my_array, 3); // explained below
}
Now let's see how to pass arrays into functions. Assume we want to have a function called some_function that receives an array.
void some_function(int *some_array, int size_of_the_array)
{
// use the array however you like here
}
The function some_function receives a pointer to an int (also known as "pointer-to-int"). The name of an array is always the address of its first element, so if a function expects a pointer to an int and you give it the name of an array of ints, you are actually giving it the address of the first element in the array (this is just C++ syntax rules). So the function now has the address of the first element in the array, it can do stuff like *some_array to access the first element in the array, but what if it wants to access the other elements? It adds 1 to the pointer it already has and then applies the * operator to it: *(some_array + 1). Let's say an int is 4 bytes, if you add 1 to a pointer-to-int, the result of this addition is a new pointer that points to a location in memory 4 bytes ahead, so *(some_array + 93) is the value in the 94th element of the array some_array (array elements are stored sequentially in memory). A shorthand notation for this is some_array[93]. So if you have int *some_array = new int[100];, then some_array is a pointer and some_array[93] is the same as *(some_array + 93), which is the 94th element in the array.
The address itself though is not enough, you also need to know the number of entries in the array so that you don't try to access an element past the end of the array. In this example, assume that some_function simply prints the contents of the array, so if you don't provide 3 as the second argument to the function then it will have no way of knowing when to stop adding 1 to the pointer it received in the first argument. Beware, however, that by passing an array to a function this way, you are not passing the function a copy of the array, you are simply telling it where to find its contents in memory.