Sign in to follow this  
suliman

pass an array? (stock c++)

Recommended Posts

suliman    1653

Hi

I want to make functions to modify general int-arrays of two dimensions such as:

int arr[10][10];

void foo(int * a, int sizeX,int sizeY){

a[2+5*sizeX]=1; //sets (x,y) is (2,5) to (value 1)

}

However this gives me

error C2109: subscript requires array or pointer type

 

I did something similar before but i cannot remember the syntax. 

Thanks for helping out!
Erik

Edited by suliman

Share this post


Link to post
Share on other sites
Ravyne    14300
Maybe its too early and I've not had my coffee, or maybe I just haven't needed this kind of syntax in too long (there are better approaches) but I don't see that there should be an error here.

Are you certain there's no typos? Nothing that interferes with the type of 'a'? That the code you have here is exactly the code you're feeding your compiler (copy&paste prefered)?

Share this post


Link to post
Share on other sites
Aardvajk    13207
Unless you have a really good reason to avoid heap allocation, I would always recommend writing a wrapper around a single dimensional std::vector that provides a 2D interface.

Multidimensional array syntax is a nightmare in C and C++.

Share this post


Link to post
Share on other sites
Ravyne    14300
At any rate, an alternative syntax for array access is *(ARRAY + Y*WIDTH + X), for you that's *(a + 5*SizeX + 2)... Though that's not much help if the compiler still believes that 'a' is anything other than an array of pointer.


While we're here, let me also remind you that the rightmost dimension of an array declaration is your X axis -- that is 'int a[Y][X]' is correct, not the other way around. You might know this, but its a common mistake and it effects exactly the kind of thing you're doing here. Likewise, in 2-dimensional loops, you usually want Y on the outside loop and X on the inside, for the same reasons.

Share this post


Link to post
Share on other sites
SmkViper    5396
I don't think you can access your two dimensional array like that, because the type decays to "int (*arr)[10]" not int*

That aside, why are you doing it this way? There is almost no reason to use raw arrays in C++ now with std::vector covering dynamically sized arrays and std::array covering compile-time-sized arrays. With those you'll end up with much safer code as they bundle up their sizes and you won't mix up which size is which or accidentally type it in wrong.

If you want to get real fancy, you can use the new array_view type from the new Guideline Support Library which will let you pass various containers to your function without caring about their type. (Despite MS providing the initial implementation it will work on GCC and Clang on Mac and Linux as well) array_view is an early implementation of something the committee is working on standardizing for the next version of C++. Edited by SmkViper

Share this post


Link to post
Share on other sites
Ravyne    14300

Unless you have a really good reason to avoid heap allocation, I would always recommend writing a wrapper around a single dimensional std::vector that provides a 2D interface.
Multidimensional array syntax is a nightmare in C and C++.

I don't think OP is doing that necessarily. They're looking for a way to pass an array of unknown size to a function. A pointer along with a length or stride is perfectly workable and efficient (though makes the interface prone to mistakes).

BTW, those mistakes are exactly the kind of thing SmkViper noted above that array_view is meant to fix, however I believe array_view only works on a single dimension -- I could be mistaken though. It's probably a good idea to adopt that as a means of passing (segments of) arrays around -- even for a sequential container like vector, array_view is likely better than an iterator pair. 

 

[EDIT] Now that I've had a chance to check, I was indeed mistaken, array_view does indeed support multiple dimensions as SmkViper points out below. 

Edited by Ravyne

Share this post


Link to post
Share on other sites
NightCreature83    5006

The safest way to do this in stock C++ is to use a template

template<class T, size_t arraySizeX, size_t arraySizeY>
void function1(T(&array)[arraySizeX][arraySizeY])
{
    for (auto counter = 0; counter < arraySizeX; ++counter)
    {
        for (auto counter1 = 0; counter1 < arraySizeY; ++counter1)
        {
            array[counter][counter1] = 111;
            std::cout << array[counter][counter1] << std::endl;
        }
    }
}
 
//Call the function
int anArray[12][3];
function1(anArray);

You can now make use of arraySizeX and arraySizeY inside of the template for the size in both directions. The function syntax looks weird but it will capture the size of the array through the compiler.  You can find more information about this at this link

Caveat though this only works for stuff that is an array, you can't pass a pointer this way, that will actually generate a compile time error.

Edited by NightCreature83

Share this post


Link to post
Share on other sites
SmkViper    5396

I was able to get it to work in VS 2015 only through type casting.

This compiles:





foo((int *)arr,10,10);
 
A c-style cast might as well be a reinterpret_cast. Unless you can get by with a simple static_cast I would be highly suspect of any such code. (And this ignores the other problems with passing the array dimensions separately)
 

Unless you have a really good reason to avoid heap allocation, I would always recommend writing a wrapper around a single dimensional std::vector that provides a 2D interface.
Multidimensional array syntax is a nightmare in C and C++.

I don't think OP is doing that necessarily. They're looking for a way to pass an array of unknown size to a function. A pointer along with a length or stride is perfectly workable and efficient (though makes the interface prone to mistakes).

BTW, those mistakes are exactly the kind of thing lkmViper noted above that array_view is meant to fix, however I believe array_view only works on a single dimension -- I could be mistaken though.


array_view works on multi-dimensional arrays. It's even smart enough to deduce the dimensions for the array from the variable in op's code since the arrays are compile-time sized. Edited by SmkViper

Share this post


Link to post
Share on other sites
MarkS    3502

I was able to get it to work in VS 2015 only through type casting.

This compiles:




foo((int *)arr,10,10);

 
A c-style cast might as well be a reinterpret_cast. Unless you can get by with a simple static_cast I would be highly suspect of any such code. (And this ignores the other problems with passing the array dimensions separately)


Nothing about the original code sample was C++, so I figured, why not?

Share this post


Link to post
Share on other sites
MarkS    3502

Because you should fix the underlying problem (the type in the function declaration), not cast away compiler errors.


I'll grant you that, but the method I posted was valid for decades. I'm curious. How bad is casting, and why is it considered bad form/dangerous now? I get that doing something stupid like casting a float to a string in the hopes that you could print the float is dumb, but in this case, it is casing a pointer to a pointer.

Share this post


Link to post
Share on other sites
phil_t    8084

I'll grant you that, but the method I posted was valid for decades. I'm curious. How bad is casting, and why is it considered bad form/dangerous now? I get that doing something stupid like casting a float to a string in the hopes that you could print the float is dumb, but in this case, it is casing a pointer to a pointer.

 

Well first, a c-style cast gives no indication as to what it's actually doing under the covers (and it's very hard to search for these kind of casts in code). For pointer to pointer, under the covers it'll actually end up doing either a static_cast or reinterpret_cast depending on what the two types are.

 

A static_cast is "safer" in that it requires the two pointer types to be related, and applies the correct pointer arithmetic as needed. A reinterpret_cast is "more dangerous" in that it converts from any type to another without requiring the types be related and without changing the numerical value. You're essentially losing any type safety, and so seeing a reinterpret_cast should call special attention to that piece of code. I think the only place I ever use it is when calling legacy C apis that take pointers as integers and such.

 

So at the very least, you may as well use c++ casts - and in this case a reinterpret_cast will call attention to the fact that you're doing something sketchy (although it is correct).

 

A c-style cast could bite you if you had code that relied on the compiler treating it as static_cast, say; and then you changed one of the types causing the cast to actually be like a reinterpret_cast. Now you might have unexpectedly different pointer values as a result (whereas if you had been using an actual static_cast, you'd now be alerted to the problem by a compile error). Can't think of a good example off-hand....

Edited by phil_t

Share this post


Link to post
Share on other sites
SmkViper    5396

Nothing about the original code sample was C++, so I figured, why not?


Aside from all the reasons others posted, the OP specifically said "C++" in their post.

Good C++ practice generally avoids writing C when C++ has a better (and safer) alternative.

Essentially, it's the same reason we don't code in binary anymore. We have tools that not only make things easier, but which can check our work for us and prevent us from making mistakes without any loss of speed or storage, so why not use them?

In some cases using specific C++ features instead of the old C versions actually makes it easier for the compiler to optimize your code because your intentions are much clearer to the compiler. I.e. ranged-for loops can be better optimized than regular for loops because the compiler is allowed to make additional assumptions about how they operate and what kinds of operations are valid inside said loops. Edited by SmkViper

Share this post


Link to post
Share on other sites
Norman Barrows    7179

while the vector version will be much safer, i believe the correct old school syntax should be:

 

void foo(int * a, int sizeX, int sizeY, int value)

{

a[sizeY*sizeof(int)+sizeX]=value;

}

 

 

void main()

{

int arr[10*10];

foo(arr,2,5,1);
}
 
 
you declared arr as a two dimensional array, but in foo, you try to address it as a one dimensional array.

Share this post


Link to post
Share on other sites
suliman    1653

Yes i want to be able to pass all my different arrays (typically 2-dimensions) to the function.

But I only know how to make the function work with 1-dimensional arrays (as it takes a pointer).

 

It works as intented for me now (albeigt it might be somewhat ugly)

Share this post


Link to post
Share on other sites
MarkS    3502

Yes i want to be able to pass all my different arrays (typically 2-dimensions) to the function.

But I only know how to make the function work with 1-dimensional arrays (as it takes a pointer).

 

It works as intented for me now (albeigt it might be somewhat ugly)

Why use two dimensional arrays in the first place? You don't gain much, if anything, from using a multi-dimensional array.

Share this post


Link to post
Share on other sites
Norman Barrows    7179


Yes i want to be able to pass all my different arrays (typically 2-dimensions) to the function.

 

address resolution will depend on the dimensionality of the array.

 

1D:

int array[max_x];

array[x]=value;

 

2D:

int array[max_y][max_x];  or int array[max_y*max_x];

array[y*max_x+x]=value;

 

3D:

int array[max_z][max_y][max_x];   or int array[max_z*max_y*max_x];

array[z*max_y*max_x+y*max_x+x]=value;                                                 (i _think_ that's right, i prefer multi dimensional arrays to avoid such BS math junk)  

 

and you'll need to know the size of the array to resolve the address. so you end up passing array, x, y, max_x, and max_y for both one and two dimensional arrays!

 

of course you can hard code max_x and max_y so you don't have to pass them, but we all know hard coded magic numbers are frowned upon. if you change your array size you break your code.

 

so you might as well have two methods, one for 2D arrays and one for 1D.

 

when you have an API like that, with 1d and 2d versions, studies show its better to do a 1d api and a 2d api, rather than an all-in-one 1d and 2d API. with the all in one API you end up passing data or flags you don't have to.

 

 

or better yet, why bother with setter methods at all when you can just do array[x][y]=value and get on with life?  you might be over-engineering things. is there some reason why a simple assignment statement won't work?

Share this post


Link to post
Share on other sites
Ravyne    14300


while the vector version will be much safer, i believe the correct old school syntax should be:
 
void foo(int * a, int sizeX, int sizeY, int value)
{
a[sizeY*sizeof(int)+sizeX]=value;
}

 

No need for sizeof(int) at all. No need for sizeY at all. You need the stride (sizeX), and the indices of the elements you intend to access. You got this right in your second post: array[y*max_x+x]=value;

 


and you'll need to know the size of the array to resolve the address. so you end up passing array, x, y, max_x, and max_y for both one and two dimensional arrays!

 

You don't need sizeY for accessing elements -- You need it for bounds checking, if that's your bag -- but sizeX is sufficient for access.

Share this post


Link to post
Share on other sites
cozzie    5029
Before diving into the "how", what do you want to achieve?
Knowing this might help in finding the best solution, being a pointer, const ref to a struct/ class, std::vector or whatsoever.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this