Reputation: 593
I'm learning about pointers and I can't get this code to work. Here's what I have so far:
void incrementArray(int* a[]) {
for(auto& x : a) {
++x;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int array[] = {0,1,2,3,4,5,6,7,8,9};
for(auto x : array) {
cout << x << '\n';
}
incrementArray(&array);
for(auto x : array) {
cout << x << '\n';
}
}
I'm getting the following error:
'incrementArray' : cannot convert parameter 1 from 'int (*)[10]' to 'int *[]'
What can I do to fix my code?
Upvotes: 1
Views: 3567
Reputation: 672
There are a couple approaches you could take to increment the elements of an array, all of which require knowing where to start and where to end. The simple way of doing what you want is to just pass the start and end address pointers, but you could also pass a start address with some offset. Since you are using a C-Style array, your int
element has and address int*
, so your std::begin(array)
is an int*
to the first element while std::end(array)
points to the address of the location after your last allocated element. In your program, the std::end()
address points to the memory location after your 10th allocated element. If you had an array with a size allocation (int other_arr[40]), std::end()
will point to the first address after the allocation (std::end(other_arr)
would be std::begin(other_arr)+41
). C++ has recently introduced non-member std::begin()
and std::end()
in the <iterator>
library, which returns a pointer to the respective element locations in your C-Array.
#include <algorithm> // std::copy
#include <iostream> // std::cout
#include <iterator> // std::begin
void increment_elements(int* begin, const int* end) {
while (begin != end) {
++(*begin);
++begin;
}
}
// An increment functor for std::transform
int increase_element(int i) {
return ++i;
}
int main() {
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (const int x : array) {
std::cout << x << ' ';
}
std::cout << '\n';
increment_elements(std::begin(array), std::end(array));
// Another way to write your print statement above
std::copy(std::begin(array),
std::end(array),
std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
// Transform array elements through increase_element()
// and print result to cout.
std::transform(std::begin(array),
std::end(array),
std::ostream_iterator<int>(std::cout, " "),
increase_element);
std::cout << '\n';
}
The generalized version of the increment_elements()
function can be found in the <algorithm>
library as the function std::transform()
documented here.
Since you are learning now, here are some habits that you can start to utilize:
Do not use using namespace std;
at the global level. By pulling everything in the standard library into the global namespace, you "pollute" it with functionality that can be called if a function call for it exists, since it doesn't require a std::
prefix qualification. Say you were to write a function that calculated the euclidean distance between two (x,y) points, double distance(Point* p1, Point* p2)
. You decide to use any of the STL containers, such as <vector>
. The containers utilize the <iterator>
library, which has its own std::distance(T*, T*)
function to calculate the distance between two addresses in memory. By bringing std
into the global namespace by using namespace std;
, you now have 2 functions with the same signature in the same namespace, that do 2 completely different things. This is very bad yet easily avoidable. This general guideline is probably unnecessary for small projects, but I still recommend you just don't ever do it for any project. Ever.
const
or const T&
your read only operations. When doing operations where you are pulling data for reading and you don't want to modify the data, initialize using const
or const T&
. const
by itself is sufficient for primitive datatypes (int, float, double, char), but non-primitives will require const T&
(T
is the type). Values that are of type const T&
(called const referenced) are read-only (const
) and directly accessed (&
).
Upvotes: 1
Reputation: 7284
What you are trying to do will not work since the signature of a function taking int a[]
as an argument does not contain the necessary length information needed to write a for-each loop (i.e. to instantiate the begin()
and end()
templates needed to use the for-each syntax). GCC's warning says this fairly clearly:
Error:(14, 19) cannot build range expression with array function parameter 'a' since
parameter with array type 'int *[]' is treated as pointer type 'int **'
I thought this might be do-able with a template, but . . .
EDIT:
It can be done with templates, just took me a moment to wrap my head around the syntax. Here is your example in working condition:
template <size_t N>
void incArray(int (&a)[N]) {
for(auto& x : a) {
++x;
}
}
int main(int argc, const char * argv[])
{
int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto x : array) {
cout << x << " ";
}
cout << endl;
incArray(array);
for (auto x : array) {
cout << x << " ";
}
cout << endl;
return 0;
}
Upvotes: 1
Reputation: 21763
C-style arrays have funny syntax. To pass the array to a function, use int a[]
This does not copy the array and changes to the array inside the function will modify the external array. You only need to call incrementArray(array);
no & needed
You could try using std::array
class which follows more normal syntax.
Upvotes: 3
Reputation: 3067
you have a pointer as a parameter (a reference to an array), but you wish to modify the actual thing it's pointing to, so you gotta change *a, not a.
You could use an array, vector, list, etc object that would have methods already associated to them that do most of the manipulation you could want
Upvotes: 2