I'm learning C++ from a course on Udacity.
Can you explain to me why setGrades() was defined as a pass-by-pointer-to-value function? Why is there an error with passing by value? In the code below, I omitted the definition for printGrades() and setID().
#include<iostream>
using namespace std;
const int SIZE = 5;
template <class T>
class StudentRecord
{
private:
const int size = SIZE;
T grades[SIZE];
int studentId;
public:
StudentRecord(T defaultInput);
void setGrades(T* input);
void setId(int idIn);
void printGrades();
};
template<class T>
StudentRecord<T>::StudentRecord(T defaultInput)
{
for(int i=0; i<SIZE; ++i)
grades[i] = defaultInput;
}
template<class T>
void StudentRecord<T>::setGrades(T* input)
{
for(int i=0; i<SIZE;++i)
{
grades[i] = input[i];
}
}
int main()
{
StudentRecord<int> srInt(-1);
srInt.setId(111111);
int arrayInt[SIZE]={4,3,2,1,4};
srInt.setGrades(arrayInt);
srInt.printGrades();
return 0;
}
The output is supposed to be:
ID# 111111: 4
3
2
1
4
C++ does not allow passing builtin C-style arrays by value. One can pass an array by reference or pass a pointer to the first element of the array. Given such pointer, the entire array can be accessed.
Passing-by-pointer is not a usual term in the literature but people keep coining similar terms time and again, which shows some kind of genuine need. The idea behind the term is as follows: one passes a pointer by value but the goal is to let the function access the pointed-to object as an lvalue (which is normally achieved by passing that object by reference).
That's simply because it is an array you want to give to setGrades because you want to set all SIZE values of the array 'grades'.
As you know perhaps, the name of an array can be used like a pointer to the first value of that array.
For example you could write *arrayInt as a term which is equivalent to arrayInt[0].
So when you pass an pointer to the first element of the array to setGrades, the function can get the other elements of that array with arrayName[i] where i is in between 0 and SIZE.
You want to pass an array to setGrades, you can also define it like this:
void StudentRecord<T>::setGrades(T input[])
However the compiler will convert it to a T* pointer automatically.
Functions will always make its own copy of all parameters you gave during compilation (see: call by value). Right here it's an array, but the compiler can't assign an array to another immediately. The only way to do array assignment is to assign its element one by one (or copy the entire memory chunk of the array), and compiler won't do that. The compiler do know that you can access the entire array if you got a pointer of type T pointed to the first element of the array. It's the alternative way of passing the entire array.
And that is the most common way to pass an array to a function.
Related
So I have several questions. First how do I pass a 3D array into a function. I need to pass the whole array as the function is to run a loop to output the contents of the array to a file. This is what I currently have
int array[5][3][3]
void function(int a[5][3][3])
{
//...
}
void function(array); //or void function(array[5][3][3]);
I have found a way to make it work using pointers to the array, however I have asked my teacher and he does not want us to use pointers.
My second question is if I plan to modify a global variable inside a function, I do not need to pass it to the function? I can just use it inside the function as I would inside main?
Yet another problem I am having now is passing a single value from an array into a function.
In a loop I need to pull a value from an array[i][j][2] (i and j being indexes of an outer and inner loop) and pass it to a function to evaluate whether or not it is greater than 90. This is for a school assignment, so understand there are certain specifications I have to meet. (Like not using pointers, and passing a whole array, and passing one value from an array, because as a class we have not yet learned how to use pointers)
Your code is correct, but actually there no such thing as an array parameter in C++ (or in C). Silently the compiler will convert your code to the equivalent pointer type, which is
int array[5][3][3];
void function(int (*a)[3][3])
{
...
}
So although your professor told you not to use pointers, actually you cannot avoid them, because there's really no such thing as an array type parameter in C++.
Second question, the only point of globals is that you can refer to them anywhere, so no need to pass them as parameters.
For passing complex arrays I prefer to wrap them in a structure:
struct array {
int a[5][3][3];
};
void function(struct array *a) ...
This avoids a lot of pitfalls with trying to pass arrays as function arguments.
you might use a pointer instead int ***a
int array[5][3][3]
void dummy(int d[][3][3])
{
d[1][1][1] = 0;
}
you may also pass it as a void * then make
int array[5][3][2]
void function(int* b)
{
int i=0;
int j=1;
int k=2;
l[ k*(3*2)+j*(2)+i ] = 9;
}
function((int*) array);
Not exactly sure how to word the title but I'll explain as best I can.
I have a program that originally used a 2D array of a set size and so it was defined as:
typedef char Map[Row][Col];
I'm now trying to dynamically allocate memory for it and it has now also become of variable size based on input. It's now defined as:
typedef char** Map;
In my main method, I originally had:
Map map;
readUserInput(map);
Basically readUserInput takes the map array as a parameter, and assigns values to it based on user input. The map then contains values and is used in other functions.
I've updated the readUserInput function so that it dynamically sizes the array and it allocates/deallocates memory for it. This works fine, but the problem comes from the fact that now in the main method, map is not being updated. The above code in main now looks like:
Map map = nullptr;
readUserInput(map);
but after running the readUserInput function, map is still null. Inside of the function, map is updated fine, so I'm not understanding the difference made between the changes.
What you pass to function is a pointer to array and fuction can't change it. But replacing array with pointer to pointer is incorrect in most case.Pointer to pointer suggest that have a 1D array of pointers. Which may (or may not) point to other arrays. Such data organization sometimes referred to as jagged arrays, because it allows each row to be of separate length. But on practtice jagged arrays and their subclass, sparse matrices, usually implemented as 1D array to avoid re-allocation.
To avoid decaying and to actually store a monolithic array in memory, you should use 1d array and, preferably, encapsulation for pointer arithmetic and reallocation, and then pass reference to object that stores all required states. Reference ensures that object is mutable by function ( a smart-pointer-less version for an example):
class Map
{
int rows, cols;
char *data;
public:
Map() : rows(), cols(), data(nullptr) {}
Map(int r, int c) : rows(r), cols(c), data(new char[r*c]()) {}
~Map() { delete[] data; }
void resize(int r, int c) {
if(rows == r && cols == c) return;
char* tmp = new char[r*c]();
if(data)
{
// copy old data here if required
delete[] data;
}
row = r; col = c;
data = tmp;
}
char& operator() (int r, int c) { return data[r*cols + c]; }
char operator() (int r, int c) const { return data[r*cols + c]; }
};
NB: this class requires a copy and move operations to be implemented if any copy must be allowed.
The function prototype would be:
void readUserInput(Map& map);
With such class you can do dynamic resizing, store its size, and address element as simple as this:
int main()
{
Map test(4, 5); // declaring and allocating memory
test.resize(3,3); // reallocating
test(1,1) = 3; // writing
//reading
std::cout << +test(1,1) << std::endl;
}
The function should accept the array by reference in the C terms like
readUserInput( &map );
when the function is declared like
void readUserInput( Map *map );
or in the C++ terms when the function is declared like for example
void readUserInput( Map &map );
and called like
readUserInput(map);
Instead of allocating dynamically arrays you could use the container std::vector<std::string>.
The code you have used is a pure C-style code, and is prone to many mistakes:
You use typedef instead of: using Map = char**;
You use a function which gets a pointer and fills it, which is more common in C than in C++.
You use raw pointer instead of smart pointers (added in C++11), which may cause a memory leak in the end.
I've updated the readUserInput function so that it dynamically sizes the array and it allocates/deallocates memory for it.
This means that now it should be a class named Map, since it should be able to allocate/deallocate, insert and remove values, and is a valid container. Actually, you are creating a type of std::vector here, and if you don't create it for you own learning process, I strongly suggest you to use the std containers!
It is possible to pass both pointer and references in C++, notice that:
You can pass a reference only if the value isn't nullptr.
When there should be a value, reference is recommended.
In this case, your function should look like
void readUserInput(Map* map);
and should be called using:
readUserInput(&map);
I searched this question, most of them says the same thing. Since we only pass the arrays address in a function, compiler can not know the arrays size by looking at the address, they say. I tried to test this by using this code, and both functions gave the same results. So, how does specifying the arrays size as a function parameter help me in a practical way?. In which conditions does specifying the size help us?.
class ArrayTest
{
public:
void say(int ar[])
{
cout<<ar[1]<<endl;
cout<<ar[7]<<endl;
}
void say(int ar[],int sizeAn)
{
cout<<ar[1]<<endl;
cout<<ar[7]<<endl;
}
};
int main()
{
ArrayTest test;
int anAr[5] = {1,2,3,4,5};
test.say(anAr);
test.say(anAr,5);
return 0;
}
This is about you as a programmer having the chance to boundary check, not whether the compiler can do it.
Just try to print out all the elements in the array, with the size:
void say(int ar[],int sizeAn)
{
for(int i=0; i< sizeAn; ++i)
cout<<ar[i]<<endl;
}
now without the size:
void say(int ar[])
{
for(int i=0; i< /*HOW DO I KNOW NOW?*/; ++i)
cout<<ar[i]<<endl;
}
Passing array size as a function parameter is a bad idea, because if you need an array as an array in function passing its size won't have any effect. The array you passed will be decayed to a pointer. So you need to maintain array as is.
Templates provide a simple and effective way to prevent array decay while passing them as function arguments.
template<std::size_t N>
void foo(int (&your_array)[N])
{
for(int i = 0; i < N; i++)
//process array, N will be your array size.
}
//simply pass array when calling the function. N be taken automatically.
//somewhere else
int main()
{
int arr[10];
foo(arr);
}
hope this helps.
Note that your code is invoking undefined behavior because you're accessing element 7 of an array that is only 5 elements big. Using the size parameter, you could for instance check if the index is past its size and not do that call instead.
In your example, you get the same results becaue you aren't actually using the parameter:
void say(int ar[],int sizeAn)
{
cout<<ar[1]<<endl;
cout<<ar[7]<<endl;
}
sizeAn is unused, so it's not making any difference. But consider for instance the following code:
void say(int ar[],int sizeAn)
{
for (int i = 0; i < sizeAn; i++){
cout<<ar[i]<<endl;
}
}
Here, it's printing all the items in the array, so it needs to know how big the array is. If you used an std::vector, for instance, you wouldn't need to pass the size as you can just call the size function, but you can't do that with C style arrays, so you need to pass that size as a parameter if you want to write a function that behaves differently depending on the size).
Or here's a more practical example of your code where the size parameter is used to avoid the undefined behavior:
void say(int ar[],int sizeAn)
{
cout<<ar[1]<<endl;
if (sizeAn >= 8){
cout<<ar[7]<<endl;
}
}
Now it's the same as your code with the change that it's only printing the element 7 if it actually exists.
As you say, compilers can't tell how big an array is if passed to a function. Your first say function tries to reference past the end of the array (ar[7] is beyond the size of 5). Your second say function means you can length check to make sure you don't make this error.
void say(int ar[], int sizeAn)
{
if(sizeAn>1)
cout<<ar[1];endl;
if(sizeAn>7)
cout<<ar[7];endl;
}
This way, YOU know the length and the function can check it before accessing invalid memory locations.
Why do we specify arrays size as a parameter when passing to function in C++?
Do we?
Well, sometimes. The canonical way to pass a range in C++ is using an iterator-pair though, even if I can see it evolve to using ranges when the Range-TS is finally used everywhere.
Anyway, there are other ways to convey what (sub-)range we want to work with. So, let's take a look:
In-band-signalling, like NUL-terminator for c-strings.
An implicit part of the functions contract, like "it will always be exactly 12 elements".
Passing a view of the part we want. Unfortunately, until the ranges-TS is fully incorporated, standard-library-support for that is severely anemic, being restricted to std::string_view in C++17 and extended with std::span for contiguous ranges (like arrays) in C++20 (look at the guideline-support-library for now).
Using an iterator-pair. The full flexibility of iterators, though calculating the length might be costly, or impossible without consuming the range. This is the preferred way in the standard-library.
Using start-iterator and length. Also quite common, but not to the same degree, and does not allow iterators determining the length as you iterate, not that that is an issue here.
Using a (constant where appropriate) reference to the whole container or range, probably templated for generality. This might be combined with point 3, but need not.
Of those, if you know the element-type, and restrict to contiguous arrays, pointer+length is the most comfortable and flexible to use for now, which does not need different code for different lengths, so that's that.
ok so suppose I have a function myFunction. Then in main i have a multi dimensional array of pointers. I want to pass a pointer to this array of pointers into myFunction. How would I do that? I know that If you want to pass an int to my function, one can write the function as
myfunct( int x) { ...}
What would that type of x be if I have to pass a pointer to an array of pointers? Thanks in advance :D
Typically you want to modify the elements of an array rather then the actual pointer. The actual pointer is given by malloc and if you change it, by writing directly to the value, it won't affect the memory allocation (except you might loose the initial pointer...).
This might be what you're looking for in a 2D array.
void myfunct(int** ptr,int items, int array_items)
{
//some code
}
int main(int argc, char *argv[])
{
const auto items = 5;
const auto array_items = 7;
int** multi_dimensional_array = reinterpret_cast<int**>(std::malloc(items * sizeof(int*)));
for (auto i = 0 ;i < items;++i)
{
multi_dimensional_array[i] = static_cast<int*>(std::malloc(sizeof(int) * array_items));
}
myfunct(multi_dimensional_array,items,array_items);
//deallocate
}
Wrap your multidimensional array inside a class. That way you can carry the data and dimensions in one block and passing it around is as simple as moving around any other class.
Remember to observe the Rules of Three, Five, and Zero, whichever best applies to how you store your array inside your class. std::vector is a personal favourite because it allows you to use the Rule of Zero.
For example:
#include <iostream>
#include <vector>
struct unspecified
{
};
template<class TYPE>
class TwoDee{
int rows;
int cols;
std::vector<TYPE> data;
public:
TwoDee(int row, int col):rows(row), cols(col), data(rows*cols)
{
// does nothing. All of the heavy lifting was in the initializer
}
// std::vector eliminates the need for destructor, assignment operators, and copy
//and move constructors. All hail the Rule of Zero!
//add a convenience method for easy access to the vector
TYPE & operator()(size_t row, size_t col)
{
return data[row*cols+col];
}
TYPE operator()(size_t row, size_t col) const
{
return data[row*cols+col];
}
};
void function(TwoDee<unspecified *> & matrix)
{
// does stuff to matrix
}
int main()
{
TwoDee<unspecified *> test(10,10);
function(test);
}
To directly answer your question, typically the type passed will be int * for a vector of int, and int ** for a 2D array of int
void myfunct( int **x)
{
x[2][1] = 25;
return;
}
If for some reason you wanted that to be an array of int pointers instead of int you need an extra *.
void myfunct( int ***x)
{
*(x[2][1]) = 25;
return;
}
Let me first try to interpret the exact type that you want to deal with. I suppose in your main function there is a "multidimensional array" which stores pointers for each element. As an example, let's say you have a 3-dimensional array of pointer to integer type.
Assume that you know the size of the array:
C style array will look like this:
int *a[4][3][2];
that means a is a 4x3x2 array, and each element in the array is a pointer to integer. So overall you now have 24 pointers to integer in total, as can be seen by testing the result of sizeof(a) / sizeof(int*) (the result should be 24). Okay, so far so good. But now I guess what you want is a pointer to the array a mentioned above, say b, so b is defined
int *(*b)[4][3][2] = &a;
Notice that although now b looks intimidating, in the end it is just a pointer which just stores an address, and sizeof(b) / sizeof(int*) gives 1 as the result. (The * inside parenthesis indicates b is pointer type, so b is a pointer to a "multidimensional array" of pointers to integer.)
Now to pass b to myFunction, just give the same type of b as argument type in the declaration:
void myFunction(int *(*x)[4][3][2]) {
// do something
}
And that's it! You can directly use myFunction(b) to invoke this function. Also, you can test that inside myFunction, x is still of the size of one pointer, and *x is of the size of 24 pointers.
*Note that since we are passing a pointer to array type into the function, the array-to-pointer decay does not apply here.
Assume you don't know the size of the array at compile time:
Say you have int N1 = 4, N2 = 3, N3 = 2; and you want to initialize a N1xN2xN3 array of pointer to integer, you cannot directly do that on the stack.
You could initialize use new or malloc as suggested in #Mikhail's answer, but that approach takes nested loops for multidimensional arrays and you need to do nested loops again when freeing the memory. So as #user4581301 suggests, std::vector provides a good wrapper for dynamic size array, which do not need us to free the memory by ourselves. Yeah!
The desired array a can be written this way (still looks kind of ugly, but without explicit loops and bother of freeing memory)
std::vector<std::vector<std::vector<int*>>> a (N1,
std::vector<std::vector<int*>> (N2,
std::vector<int*> (N3)
)
);
Now, b (the pointer to a) can be written as
auto *b = &a;
You can now pass b with
void myFunction(std::vector<std::vector<std::vector<int*>>>* x) {
// do something
}
Notice that the * before x means x is a pointer.
I am currently working on a program that requires a function to figure out array values and then stores those values in arr1[], which has an unknown size and is calculated within the function. It then passes back the entire array along with the size of the array back to the main function. Using a struct, I returned the size, but I cannot return the array for some reason. Can someone please direct me in the right direction with what I'm doing wrong?
Here is my struct:
struct Arr
{
int size_1;
int arr_1[];
};
And here is part of my function that returns the array, where arr1[] is the array in which I need to return:
Arr smallElement(int arr[], int size)
{
Arr tempArr;
for (int count = 0; count < newSize; count++)
{
tempArr.arr_1[count] = arr1[count];
}
return tempArr;
}
This is what I use in my main function to call the function to print the array, but it just prints 3 random numbers every time:
Arr a;
a = smallElement(array, n);
cout << "The array is: ";
for (int count = 0; count < a.size_1; count++)
{
cout << a.arr_1[count] << " ";
}
Inside a struct, int arr_1[] does not define an actual array. It is actually just a placeholder called a flexible array member. In C, this is used by allocating memory for the fully defined part of the struct plus additional space for the array—you have to manually add space when using malloc or other memory allocation. In standard C++, you should not use this. (It is not part of standard C++. It is an extension adopted by some compilers from C, where it is standard.)
When you declared tempArr, it created just the fully defined portion of the struct. When you assigned values to tempArr.arr_1[count], there was no allocated array there, so the behavior is undefined. When you return tempArr, a copy of the struct is returned, but, again, only a copy of the defined portion.
If the caller knows the size the array will be, you may want to have them pass the array to the function, perhaps by allocating it with new or by using std::vector and passing the vector by reference. If the size is not known by the caller, then you may want to have the function allocate the array and return a pointer to it (in which case the caller is responsible for deleting it later), or you may want to pass an empty (or otherwise available for re-use) std::vector to the function, preferably by reference. Inside the function, it can add more elements to the vector, and the std::vector will manage memory allocation for you.