How to access elements of an array from a function [closed] - c++

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
The foreach loop type doesn't work if the array being iterated has come from a parameter and was declared in another scope.
// C++ program to demonstrate use of foreach
#include <iostream>
using namespace std;
void display(int arr[])
{
for (int x : arr)
cout << x << endl;
}
int main()
{
int arr[] = { 10, 20, 30, 40 };
display(arr);
}
Here's what the error looks like:
What are begin and end?
What is happening behind the scenes that gives this error?
Is the for loop implicitly changed to an iterator declaration before running? is this because C style arrays are not container objects and therefore don't have begin and end? If this is true then why does this code work:
#include <iostream>
using namespace std;
int main()
{
int arr[] = { 10, 20, 30, 40 };
for (int x : arr)
cout << x << endl;
}
Is there some information decaying happening when passed as an argument?

That for loop is called a range-based for loop. The compiler is transforming that code into something else. You can see all the details here https://en.cppreference.com/w/cpp/language/range-for (it's different for C++11, C++17, and C++20).
However, in principle, your code becomes this:
{
auto && __range = arr;
auto __begin = begin_expr;
auto __end = end_expr;
for ( ; __begin != __end; ++__begin) {
x = *__begin;
cout << x << endl;
}
}
The thing here is that begin_expr and end_expr can be different things. If arr was an array, like int arr[3], __range would be __range + 3. If arr was a class with members begin() and end() it would be __range.begin(). Otherwise, it would be begin(__range).
This last case is were your code is landing, because arr is not an array but a pointer to int (int [] arr means int* arr). And there is no begin() and end() for int*.
It works here, because in this example, arr is an array of 4 integers:
int arr[] = { 10, 20, 30, 40 };
for (int x : arr)
cout << x << endl;
There are different ways to do this. This is how I'd prefer to do it myself:
template <typename T>
void display(std::vector<T> const & arr)
{
for (int x : arr)
cout << x << endl;
}
int main()
{
std::vector<int> arr { 10, 20, 30, 40 };
display(arr);
}
You can check https://cppinsights.io/ to see how the compiler transforms your code (to some degree). For instance, your code becomes this (mind that it does not work if the code does not compile, so I had to comment out the loop):
#include <iostream>
using namespace std;
void display(int * arr) // <-- notice int*
{
}
int main()
{
int arr[4] = {10, 20, 30, 40}; // <-- notice int[4]
display(arr);
}
And if you take this code:
int main()
{
int arr[] = { 10, 20, 30, 40 };
for (int x : arr)
cout << x << endl;
}
the result is this:
int main()
{
int arr[4] = {10, 20, 30, 40};
{
int (&__range1)[4] = arr;
int * __begin1 = __range1;
int * __end1 = __range1 + 4L;
for(; __begin1 != __end1; ++__begin1)
{
int x = *__begin1;
std::cout.operator<<(x).operator<<(std::endl);
}
}
}

The first piece of code doesn't work, because
void display(int arr[])
is actually equivalent to
void display(int *arr)
and you can't use a single pointer for iteration, because it doesn't have any length information. See also What is array to pointer decay.
The second piece of code works because the range-expression in a range-based for must be
any expression that represents a suitable sequence (either an array or an object for which begin and end member functions or free functions are defined, see below) or a braced-init-list.
Source
So instead of the non-existing member functions arr.begin() and arr.end() it can use the free functions std::begin() and std::end() which are overloaded for arrays.

first of all, let me suggest you to edit your question as #Ted mentions in his comment.
I must admit the error message is not quite intuitive. The appearance of missing begin and end stems from the definition of range-based for loop. As you can see, you're passing an array of unknown size as the range_expression, right? Linked page has further to say about this case:
If range_expression is an expression of array type, then begin_expr is
__range and end_expr is (__range + __bound), where __bound is the number of elements in the array (if the array has unknown size or is
of an incomplete type, the program is ill-formed)
Your second code snippet actually uses an array with known size, since its use is in the same scope as its initialization. Array initialization states:
When initializing an array of unknown size, the largest subscript for
which an initializer is specified determines the size of the array
being declared.
Let me offer you a standard library template for better grip on arrays. It is .. err .. std::array :). What you get is basically value semantics and consistence with other types:
accepting argument by value makes a copy of the whole content,
accepting argument by (const) reference has a familiar syntax,
you don't implicitly lose the size information when passing an array object.
So your code will happen to look like this:
#include <array>
#include <iostream>
using namespace std;
void display(const array<int>& arr)
{
for (int x : arr)
cout << x << endl;
}
int main()
{
std::array arr{ 10, 20, 30, 40 };
display(arr);
}

When you give a C-style array to a function, you need to give its size as well because the function is not aware of the array's size. The array argument int x[] is considered as a pointer on the array's first element (thus like an int* x). In consequence, the function can't tell how big the array is as it only has the pointer (the address of the first element). You need to specify how many int (or other types) from the provided adress the function shall read to obtain valid elements.
#include <iostream>
using namespace std;
void display(int arr[], int nsize)
{
int i=0;
while (i < nsize){
cout << *arr << endl;
// you move the pointer to the next element to be read
arr++;
i++;
}
}
int main()
{
int arr[] = { 10, 20, 30, 40 };
int n= sizeof(arr)/sizeof(int);
display(arr, n);
}

Related

Getting mismatched types error when i tried to assign numeric value to array object using pointer

I have initiated an array of 6 elements and tried to print it using a function called 'print'. I have used array object from the stl library. I passed the address of the array object to the print function. When I tried to change the value of the array object in the print function I am getting mismatched types error.
#include <bits/stdc++.h>
using namespace std;
void print(array<int, 6> *arr){
for(int i : *arr){
cout<<i<<" ";
}
*(*arr+2)=2;
cout<<endl;
}
int main(){
array<int, 6> arr2={1, 2, 3, 4, 5, 6};
print(&arr2);
print(&arr2);
}
In *(*arr+2)=2; you deference the array pointer and try to add 2 to it and then dereference that result to assign 2. I assume you want to assign 2 to the element at index 2 in the array.
You do not need to use pointers here though, take the array by reference.
And, never #include <bits/stdc++.h>.
#include <array> // include the proper header files
#include <iostream>
void print(std::array<int, 6>& arr) { // by reference
for (int i : arr) {
std::cout << i << ' ';
}
arr[2] = 2; // assign 2 to the element at index 2
std::cout << '\n'; // std::endl flushes the stream which is usually uncessary
}
int main() {
std::array<int, 6> arr2 = {1, 2, 3, 4, 5, 6};
print(arr2); // and don't take the array address here
print(arr2);
}
If you really want to use a pointer here, this could be an option:
#include <array>
#include <iostream>
void print(std::array<int, 6>* arr_ptr) { // pointer
if (arr_ptr == nullptr) return; // check that it's pointing at something
std::array<int, 6>& arr = *arr_ptr; // dereference it
for (int i : arr) {
std::cout << i << ' ';
}
arr[2] = 2;
std::cout << '\n';
}
int main() {
std::array<int, 6> arr2 = {1, 2, 3, 4, 5, 6};
print(&arr2);
print(&arr2);
}
This statement
*(*arr+2)=2;
does not make a sense. For example the operator + is not defined for the class template std::array and as a result this expression *arr+2 is invalid.
You could write
( *arr )[2] = 2;
or for example
*( ( *arr ).begin() + 2 ) = 2;
*(*arr+2)=2;
Would be equivalent to
(*arr)[2] = 2;
if arr were a pointer to an array. I assume that's what you are looking for. But a std::array instance is not an array in that sense. It is an object encapsulating an array, and emulating some of the properties of the underlying array, but it cannot be used interchangeably with the underlying array. In particular, std::array objects do not decay to pointers as actual arrays do, and your code appears to be trying to rely on that.
You could instead do
*((*arr).data() + 2) = 2;
or, more idiomatically,
*(arr->data() + 2) = 2;
to leverage that array-to-pointer decay. Or you could do
arr->data()[2] = 2;
. But none of those is as clear or straightforward as the ...
(*arr)[2] = 2;
... already mentioned, which is what you should do if you must work with a pointer to a std::array instead of a reference to one.
First note that std::array has no operator+. So when you wrote the expression:
*arr+2 // INCORRECT
In the above expression, you are dereferencing the pointer to std::array and then adding 2 to the result. But as i said at the beginning, that there is no operator+ for std::array, this expression is incorrect.
Limitation
Second you program has a limitation which is that the function print can accept a pointer to an std::array with only 6 elements. So if you try to pass a pointer to an std::array of some different size then your program won't work.
Solution
You can fix these by:
Passing the std::array by reference.
Using templates. In particular, using template nontype parameter.
The advantage of using template nontype parameter is that now you can call the function print with an std::array of different size as shown below.
#include <iostream>
#include <array>
template<std::size_t N>
void print(std::array<int, N> &arr){ //by reference
for(const int &i : arr){
std::cout<<i<<" ";
}
arr.at(2) = 2; //use at() member function so that you don't get undefined behavior
std::cout<<std::endl;
}
int main(){
std::array<int, 6> arr2={1, 2, 3, 4, 5, 6};
print(arr2);
std::array<int, 10> arr3 = {1,2,4,3,5,6,7,8,9,10};
print(arr3);
}
Some of the modifications that i made include:
Removed #include <bits/stdc++.h> and only included headers that are needed.
Used templates. In particular, removed the limitation by using template nontype parameter.
Passed the std::array by reference. That is, there is no need to pass a pointer to an std::array. We can just pass the std::array by reference.
Used at() member function so that we don't go out of bounds and so that we don't get undefined behavior.

Print addresses of elements in a vector in C++

I'm new to programming and I want to know if this is a valid way to print the address of each element in a vector.
#include <iostream>
#include <vector>
using namespace std;
void printAdress( vector<int> & a ) {
vector<int*> ptr;
for (int i = 0; i < a.size(); i++) {
ptr.push_back(&a[i]);
}
for (int i = 0; i < ptr.size(); i++) {
cout << ptr[i] << "\n";
}
}
int main () {
vector<int> a = {1, 2, 3, 4, 5, 6, 7, 8, 9};
printAdress(a);
return 0;
}
Is this error prone? Is there a more efficient way to do this? Am I using unnecessary memory? I'd love some feedback.
In C++, arrays (including the data of a std::vector container) are contiguous blocks of data elements. Thus, the address of the ith element of such an array (or vector) is just the address of the array's first element plus the value of i. (The addend is actually i * sizeof(element), but that size factor is automatically taken into account when performing pointer arithmetic in C++.)
So:
As mentioned in the comments, your printAdress function has no need to create a new vector (which is lost when the function returns).
There is no need to take the address of each element in the vector, once the address of its 'base' (which is available via the data() member function of the std::vector class) is known.
Here's a reasonably efficient example:
void printAdress(const std::vector<int>& a)
{
const int* base = a.data();
const size_t n = a.size();
for (size_t i = 0; i < n; ++i) std::cout << (base + i) << std::endl;
}
An alternative syntax. 'Behind the scenes' this is very likely doing the same as #Adrian Mole's answer (which usefully explains the fundamental concept of C++ pointer arithmetic), but illustrates a few of the other language features which the OP might encounter on their C++ journey:
template<typename T>
void printAddress(const std::vector<T>& v)
{
for (auto& e : v) std::cout << &e << "\n";
}
This allows the writing of:
using namespace std;
vector<int> a = { 1,2,3,4,5 };
printAddress(a);
vector<double> b = { 1.0,2.0,3.0,4.0,5.0 };
printAddress(b);
without any additional code in printAddress().
NB: This code doesn't compile for vector<bool> which stores the internal data in a different way.

Is it possible to pass an array into a function as a parameter without creating a variable for that array?

So I made a function that takes arrays as parameters and I've tried calling the function by passing arrays that have not been defined as variables into said function (like {0,0,0,0}). However, I am given an error which says "too many initializer values."
Say we have a function defined as:
int func(int values[]) {
int average = 0;
for(int x = 0; x < values.size(); x++) {
average += values[x];
}
return average / values.size();
}
And we want to call it without defining an array to pass in like this: func({1,6,7,2});
Is there any way to do something like this or would I have to define an array and pass it into the function that way?
You cannot do that using built-in arrays. The fact that Arrays are neither Assignable nor Copy-able. Also They are not classes so they don't have member functions like size() or they take Initializer-list.
You can achieve that through using std::array if the size is constant or using std::vector if the size if dynamic.
#include <array>
int func(const std::array<int, 5>& values) {
int average = 0;
for (size_t x{}, sz{ values.size() }; x != sz ; ++x)
average += values[x];
return average / values.size();
}
int main() {
auto ret{
func({ 1, 6, 7, 2 })
};
std::cout << ret << std::endl;
}
Also don't mix Unsigned with Signed in calculations like in your loop:
for(int x = 0; x < values.size(); x++) // x is int while values.size() is unsigned int.
int func(const std::array<int, 5>& values): pass by reference to avoid the copy especially if the size is big. Also pass by const as long as the function doesn't intend to change the parameter also another benefit of using const reference is you can pass literals instead of an object.
N.B: I recommend to also to use range-based for because it is really relevant in your example as long as you want to iterate over all the elements and not intending to insert nor to delete elements:
int average = 0;
for (const auto& e : values)
average += e;
Another version of func as #M.M pointed out is to use std::accumalate to do the job for you:
int func(const std::array<int, 5>& values) {
return std::accumulate(values.begin(), values.end(), 0) /
values.size();
}
Using a vector, yes:
#include <vector>
using namespace std;
void f( const vector <int> & v ) {
}
int main() {
f( {1,2,3,4} );
}
Arrays don't work like that. When you pass an array to a function, the address of the first element gets passed like a pointer, and inside the function there is no more information about the size of the array. (Before the compiler itself could infer the size because the array was declared in the scope, but a function can be called from any number of places)
If you want to do something like that you would either have to use a container class, such as a vector, or you could pass a second argument into the function stating the size of the array. Another way is to have some sort of end point in your array, such as is the case with c-strings, for example a null value.

Modifying elements of an array through a function

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 (&).

C++ multi dimensional array function parameter

How can I pass a two or multi dimensional array as a parameter of a function without defining its size??
Here is my example code:
void test(int *a) {
a[0][0] = 100;
}
int main() {
int a[2][2];
test(a);
cout<<a[0][0];
}
You can use a template for static sizes
template<int first, int second> void func(int(&array)[first][second]) {
}
Or a vector of vector for dynamic sizes
void func(std::vector<std::vector<int>> array) {
}
However, what you most definitely cannot do is use an int**. An int[] will decay to an int* but an int[][] will decay to an int*[]. Think about it- else, how would the language differentiate between an array of pointers, and a multi-dimensional array of values? You really should never use primitive arrays anyway, they're begging for trouble with no safety and implicit conversions up the wazoo. Grab a nice, safe std::array (or boost::array if you're in C++03) for static arrays, or std::vector for dynamic arrays.
If you're working exclusively with statically-sized, stack-allocated arrays, then a function template will do exactly what you're asking for:
#include <cstddef>
#include <ostream>
#include <iostream>
template<std::size_t N, std::size_t M>
void func(int (&arr)[N][M])
{
std::cout << "int[" << N << "][" << M << "]\n";
for (std::size_t n = 0; n != N; ++n)
for (std::size_t m = 0; m != M; ++m)
std::cout << arr[n][m] << ' ';
std::cout << '\n' << std::endl;
}
int main()
{
int i1[2][3] = { { 4, 5, 6 }, { 7, 8, 9 } };
int i2[4][2] = { { 1, 3 }, { 5, 7 }, { 9, 11 }, { 13, 15 } };
func(i1);
func(i2);
}
Passing the pointer to the array. For example, if you have a bidimensional int array you'll need to pass int** p, along with the dimensions of the array.
For built-in arrays, you have to specify the size of all dimensions but the last dimension or indexing won't work.
If your goal is just to have a function that takes multi-dimensional arrays of any size, I'd consider boost::multi_array_ref (or boost::const_multi_array_ref)
Update:
Since passing by pointer appears to be the answer that's getting the most attention (although I think the multi_array_ref is good... unless boost isn't available or something) then here's an example that flattens the array and doesn't limit you by array dimensions (although you still need size information to make it useful)
void f(int* array /* should probably pass in the size here - in this case, 4 */)
{
array[3] = 9;
}
int main()
{
int array[2][2] = { {1,2}, {3,4} };
// Note: The array is flattened here. If you truly need to remember the multi-dimensional nature, you need to pass in enough information to specify all the dimensions... maybe something like a vector<size_t> (that's what the multi_array_ref uses). I guess if you have a limited number of dimensions then a couple size_t will work for you
test(&array[0][0]);
std::cout << array[1][1] << std::endl;
return 0;
}
int a[][]
Can be passed as:
function name(int **arr) {
//your code, you can then access it just like you would have accesses your array:
arr[3][2]
}