I'm trying to access an element of an std::array given its pointer in C++. Here's some code that illustrates my problem:
#include <iostream>
#include <array>
void func(std::array<int, 4> *);
int main()
{
std::array<int, 4> arr = {1, 0, 5, 0};
func(&arr);
}
void func(std::array<int, 4> *elements)
{
for (int i = 0; i < 4; i = i + 1)
{
std::cout << *elements[i] << std::endl;
}
}
I would expect this to print every element of the std::array on a new line. However, it doesn't even get past compiling:
main.cpp: In function ‘void func(std::array<int, 4ul>*)’:
main.cpp:16:22: error: no match for ‘operator*’ (operand type is ‘std::array<int, 4ul>’)
std::cout << *elements[i] << std::endl;
What's going on here?
Thanks!
Use
std::cout << (*elements)[i] << std::endl;
instead. Otherwise operator[] is applied first, as it has higher precedence, see http://en.cppreference.com/w/cpp/language/operator_precedence.
So you first need to dereference the pointer to get access to the first pointee, which is an array, then subsequently access the array with operator[]. Otherwise your code is parsed by the compiler as *(elements[i]), so first you get the i-th array (which of course is non-existent unless i==0), then you try to dereference it, hence the compile error.
Tip: If you're worried about copies, pass the array by const reference instead
void func(const std::array<int, 4>& elements)
Then your syntax inside the function will be "natural", i.e. elements[i] will simply denote the i-th element of the array reference elements. You'll also pass the array simply as func(arr);.
#vsoftco's answer is quite correct.
I would just like to add that it is more idiomatic in C++ to pass large objects by reference, rather than pointer:
#include <iostream>
#include <array>
// declare parameter as a reference
void func(std::array<int, 4>&);
int main()
{
std::array<int, 4> arr = {1, 0, 5, 0};
func(arr); // no need to take address
}
void func(std::array<int, 4>& elements)
{
for (int i = 0; i < 4; i = i + 1)
{
// just use as normal
std::cout << elements[i] << std::endl;
}
}
If the function will not modify the array then const reference would be more appropriate:
void func(const std::array<int, 4>& elements)
{
for (int i = 0; i < 4; i = i + 1)
{
// just use as normal
std::cout << elements[i] << std::endl;
}
}
Alternate answer
While the other answers are correct too, I'd like to suggest another variant because there are cases where rewriting the code to use references is not always possible:
std::cout << elements->at(i) << std::endl;
This is a variant that avoids the pointer-de-referencing (*element) but uses the std::array::operator->. It may also be simpler to read.
How do pointers work for an array and a vector object
For array_1
#include <iostream>
#include <array>
using namespace std;
int main(){
int a[3][4]={0,1,3,3,4,5,6,7,8,9,10,11};
//int (*pi)[4]=a;
for( auto *pi=a; pi!=a+3; ++pi ){
for( auto p=*pi; p!=*pi + 4; ++p){
cout << *p << "\n";
}
}
}
For array_2
#include <iostream>
#include <array>
using namespace std;
int main(){
int a[3][4]={0,1,3,3,4,5,6,7,8,9,10,11};
//int (*pi)[4]=a;
for( auto pi=a; pi!=a+3; ++pi ){
for( auto p=*pi; p!=*pi + 4; ++p){
cout << *p << "\n";
}
}
}
For vector_1
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> a{0,1,3,3,4,5,6,7,8,9,10,11};
for(auto i=a.begin(); i!=a.end(); ++i ){
// we can also use begin(a) and end(a)
cout << *i << "\n";
}
}
For vector_2
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> a{0,1,3,3,4,5,6,7,8,9,10,11};
for(auto *i=a.begin(); i!=a.end(); ++i ){
// we can also use begin(a) and end(a)
cout << *i << "\n";
}
}
Both codes (for array_1 and for array_2) give the same output i.e (0-11)
you can see that there is a difference in the first for loop of both pieces of code.
In the For vector_1 ,as far as I understood, I think, I am creating a pointer i to the beginning of the vector and then dereferencing it to fetch the value of that corresponding position. Now my doubt is, how come in the For array_1 and For array_2 when I use the *pi in the 2nd for loop, instead of getting dereferenced(like in the case of vector), it is fetching me the iterative values?
And why does the For vector_2 not work the same way as For vector_1?
And what is the return type of the size() and begin()/end() function for vector?
Cause when I use for(decltype(a.size()) i=0; i!=end(); ++i), it is giving me a type conversion error.
The code for array_1, for array_2 and for vector_1 gives the same output(0-10). It is the for vector_2 that is giving an error.
Thanks.
In case of vector_1 you are not creating a pointer, but an iterator - in most basic use cases, like printing vector results, iterator works and is used like a pointer, but it is not the same and shouldn't be handled the same way you handle a pointer. In case of vector_2 you do something nasty since this is the job of the auto keyword to determine whether the variable is to be a pointer or not.
The last example causes an error, because you are trying to create a pointer to iterator, and the type returned by the begin() function is an iterator (a value), not the address of the iterator object.
If you want to know the return types of functions like size(), begin(), etc. you can always refer to one of the several large C++ reference websites, for example:
www.cplusplus.com
www.cppreference.com
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?
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.
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
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;
}
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 (&).
There are examples of sorting vectors or dynamically allocated arrays but I couldn't find any help regarding static arrays. Let's say I have an array
int array[10][10];
and a compare function,
bool compare(const int (*a)[10], const int (*b)[10]);
When I call it like this,
std::sort(array, array + 10, compare);
I have compilation errors: error: cannot convert 'int*' to 'const int (*)[10]' in argument passing
I tried many ways, casting array to (void**) in sort function but then I have segmentation fault. My problem is using arrays as function parameters I guess but I couldn't figure out how to use this std::sort. Otherwise, I will have to write my own sort function.
When std::sort is called on a container of elements of type T, the comparison function needs to receive arguments of type T or const T&. In this case, you have a 2-dimensional array, so the type of elements is a 1-dimensional array int[10]. Since 1-dimensional arrays decay to pointers, compare can be:
bool compare(int a[10], int b[10]);
or equivalently:
bool compare(int *a, int *b);
This will fix the error you got, but your code still won't work: std::sort needs the container elements to be assignable (or movable in C++11), but arrays are not assignable.
You can use std::vector<std::vector<int> > instead as people have suggested. Note that your fear of performance problems is misguided: Even if sorting a two-dimensional array was possible, it would involve a lot of copying of one-dimensional arrays which would take a long time. Swapping vectors, on the other hand, is done by simply swapping pointers which is faster. In general, you should not make assumptions about performance if you haven't tested it first.
The comparison function doesn't get an iterator to the element passed but the dereferenced iterator, i.e., the value type. Thus, your comparison function would need to be declared as like the one below:
bool compare(int (&a0)[10], int (&a1)[10]);
You can verify that you can actually call it with array iterators:
compare(*(std::begin(array) + 0), *(std::begin(array) + 1));
However, this won't make it possible to sort you arrays: built-in arrays are not copy-assignable. The easiest way to sort statically sized arrays (where the outer dimension flexible) is to use std::array<T, N>:
std::array<int, 10> array[10];
std::sort(std::begin(array), std::end(array));
I say, if we're gonna use the STL and C++ .. lets write it in a modern style and really use the STL.
My attempt at the problem using modern c++11:
#include <vector>
#include <iostream>
#include <algorithm>
typedef std::vector<int> ArrayInt;
typedef std::vector< std::vector<int> > ArrayData;
bool compare(const ArrayInt& a, const ArrayInt& b) {
std::cout << &(a) << ' ' << &(b) << std::endl;
int sumA = std::accumulate(a.begin(), a.end(), 0);
int sumB = std::accumulate(b.begin(), b.end(), 0);
return sumA < sumB;
}
int main(int argc, char** argv) {
ArrayData array = {
{1,2,4,0,3,7,6,8,3,3},
{13,2,4,0,3,7,6,8,3,3},
{10,2,4,0,3,7,6,8,3,3},
{1,2,4,0,3,7,6,8,3,3},
{16,2,4,0,3,7,6,8,3,3},
{1,2,400,0,3,7,6,8,3,3},
{1,2,4,0,3,7,6,8,3,3},
{120,2,4,0,3,7,6,8,3,3},
{1,2,4,0,3,7,6,8,3,3},
{1,2,4,0,3,7,6,8,3,3}
};
std::sort(array.begin(), array.end(), compare);
for (auto row : array) {
for (int num : row)
std::cout << num << ' ';
std::cout << std::endl;
}
}
It uses accumulate to sum each sub array, and sorts on the sum .. it's super inefficient because it has to sum the same row multiple times .. but it's just there to show off a custom compare function.
As an exercise, I wrote this version that uses async to distribute the summing part over any available cores to do the summing, before the sort. I'm sorry it's getting a bit off topic. I hope it's still useful to some people:
#include <vector>
#include <iostream>
#include <algorithm>
#include <future>
typedef std::vector<int> IntRow;
typedef std::pair<int, IntRow> DataRow;
typedef std::vector<DataRow> DataTable;
int main(int argc, char** argv) {
// Holds the sum of each row, plus the data itself
DataTable array = {
{0, {1,2,4,0,3,7,6,8,3,3}},
{0, {13,2,4,0,3,7,6,8,3,3}},
{0, {10,2,4,0,3,7,6,8,3,3}},
{0, {1,2,4,0,3,7,6,8,3,3}},
{0, {16,2,4,0,3,7,6,8,3,3}},
{0, {1,2,400,0,3,7,6,8,3,3}},
{0, {1,2,4,0,3,7,6,8,3,3}},
{0, {120,2,4,0,3,7,6,8,3,3}},
{0, {1,2,4,0,3,7,6,8,3,3}},
{0, {1,2,4,0,3,7,6,8,3,3}}
};
// Make use of multiple cores if it's efficient enough
// get the sum of each data row
std::vector<std::future<int>> sums(array.size());
auto next = sums.begin();
for (auto& row : array)
*next++ = std::async([](const IntRow& row) { return std::accumulate(row.begin(), row.end(), 0); }, row.second);
// Get the results
auto nextRow = array.begin();
for (auto& sum: sums)
(*nextRow++).first = sum.get();
// Sort it
std::sort(array.begin(), array.end(),
[](const DataRow& a, const DataRow& b) { return a.first < b.first; });
// Print it
for (auto row : array) {
for (int num : row.second)
std::cout << num << ' ';
std::cout << std::endl;
}
}
It needs to be compiled with pthread library or similar:
g++ -O6 sort.cpp --std=c++11 -g -lpthread