I have one quick question about the passing of arrays in C++ which I don't understand.
Basically when you want to pass a array of type integer to another function you have to pass an address to that array instead of directly passing the whole block of contiguous memory. Exactly why is the case?
Also, why is that char arrays can directly be passed to another function in C++ without the need to pass an address instead??
I have tried looking for learning materials for this online (such as cplusplus.com) but I haven't managed to find and explanation for this.
Thanks for your time, Dan.
As long as C++ is concerned, passing char arrays and int arrays are same.
There are 2 ways to pass arrays in c++.
Address is passed
int fn(int *arrays, int len);
int fn(int arrays[], int len); // Similar to above, still staying as sytax hangover from anci c
Array reference is passed
int fn(int (&array)[SIZE]); // Actual array passed as reference
You can templatized above function as
template<size_t SIZE>
int fn(int (&array)[SIZE]);
Above method allows you to pass array of anysize to this function. But beware, a different function is created from template for each size. If your function's side effect changes a local state (static variable for ex), this should be used with care.
If you don't want to change contents, use const with arguments.
If you want a copy of array in function argument, consider using stl container like std::array or std::vector or embed array in your class.
It isn't entirely clear from your question exactly what you're trying and what problems you've had, but I'll try to give you useful answers anyway.
Firstly, what you're talking about is probably int[] or int* (or some other type), which isn't an array itself... its a pointer to a chunk of memory, which can be accessed as if it were an array. Because all you have is a pointer, the array has to be passed around by reference.
Secondly, passing around an array as a "whole block of contiguous memory" is rather inefficient... passing the point around might only involve moving a 32 or 64 bit value. Passing by reference is often a sensible thing with memory buffers, and you can explicitly use functions like memcpy to copy data if you needed to.
Thirdly, I don't understand what you mean about char arrays being "directly" passable, but other types of arrays cannot be. There's nothing magic about char arrays when it comes to passing or storing them... they're just arrays like any other. The principle difference is that compilers allow you to use string literals to create char arrays.
Lastly, if you're using C++11, you might want to consider the new std::array<T> class. It provides various handy facilities, including automatic memory management and keeping track of its own size. You can pass these by value, template<class T> void foo(std::array<T> bar) or by reference template<class T> void foo(std::array<T>& bar), as you like.
You can't pass any array by value. You can pass by value either a struct containing array or std::array from C++11.
Related
#include <iostream>
using namespace std;
int func(int ar[5]){
//the code is written here with returning whatever the requirement is
}
int main(){
int ar[5];
func(ar);
return 0;
}
In this kind of situation where we are passing array through a function why the address of the array is used in actual parameter whereas the array in formal parameter?
This is because C handled arrays weirdly.
In C, arrays convert to a pointer to their first element at the drop of a hat, cannot be passed as arguments to a function, cannot be returned from functions, and cannot be copied by assignment.
C++, originally based off C, carries these screwed up design decisions as a legacy issue. Fixing them would break insane amounts of code.
If you want an array that behaves more reasonable, use std::array.
void foo(int[5]);
this is actually taking an int* argument.
int arr[7];
foo(arr);
this converts the 7 element arr to a pointer and passes it to foo. Which seems to take int[5] but that 5 does nothing.
Yes this is utterly crazy.
Now,
void bar(int(&)[5])
because C does not have references, the above is an actual reference to an array if 5 elements, so
bar(arr)
won't compile.
There is nothing weird, nor screwed up about how arrays are passed in C. C is expressed pretty simply, really. C /does/ have references; they are explicit, so, easy to see when they're in use. We call them pointers.
One simply needs to understand that there is not dedicated storage for array types, that arrays are not first-class types, so, won't behave like first-class types. Can't be assigned into (directly), nor passed into and out of functions directly.
In C/C++ we use pointers to refer to a chunk of memory where the contents of the array are stored.
So, all that's left is that we understand that when we declare an array, the name of the array is really a pointer to the beginning of the array.
int a[12] ;
/* a's type is this: */
int *a ;
So, when we "pass an array" to a function, we are copying the pointer into the function. The semantics are straightforward, and entirely consistent (unlike Java).
The array, itself, is never passed directly.
Finally, realise that there is no difference between a pointer to a single integer, and a pointer to a contiguous chunk of integers in memory.
Remember, a[i] is equivalent to *(a+i). So, in this example:
int i = 12 ;
int *p = &i ;
, *p is the same as *(p+0); the p can be thought of as an array of length 1.
With a slight change in perspective you should be able to see the simplicity, the elegance, that is C.
How to find the size of string array passed to a function. The size should be computed inside the function.
#include<iostream>
using namespace std;
template <typename T,unsigned S>
unsigned arraysize(const T (&v)[S]) { return S; }
void func(string args[])
{
unsigned m=arraysize(args);
cout<<m;
}
int main()
{
string str_arr[]={"hello","foo","bar"};
func(str_arr);
}
What i dont understand is:
If the statement arraysize(str_arr) is used in main,it wouldn't pose a problem. The str_arr is an array, so str_arr acts as a pointer, so when we use arraysize(str_arr) that means we're sending the address to arraysize function.(correct me if i'm wrong).
But in function func(), i dont understand why there is a problem, i.e. the statement arraysize(args) sends the address of the string array args(or the address of pointer args).. or is it more complicated since it becomes some double pointer?? Explain?
Also please correct the above code..
str_arr is an array of strings. When you do sizeof(str_arr), you get the size of that array. However, despite the fact that args looks like an array of strings, it's not. An argument that looks like an array is really of pointer type. That is, string args[] is transformed to string* args by the compiler. When you do sizeof(args) you are simply getting the size of the pointer.
You can either pass the size of the array into the function or take a reference to the array with a template parameter size (as you did with arraysize):
template <size_t N>
void func(string (&args)[N])
{
// ...
}
There is no way to determine the size of an array when sent to a function. You also have to remember that only a pointer to the array is sent to the function, which makes it even theoretically quite implausible to calculate the array's size.
The information of the array's size is never visible in your function, as you threw it away when you decided to use string args[] for the argument. From the compiler's perspective, it's the same as string* args. You could change the function to:
template<size_t M>
void func(string (&args)[M])
{
cout<<M;
}
but it seems you already know that, right?
If the statement arraysize(str_arr) is used in main,it wouldn't pose a
problem. The str_arr is an array, so str_arr acts as a pointer, so
when we use arraysize(str_arr) that means we're sending the address to
arraysize function.(correct me if i'm wrong).
I have to correct you here. You state a correct premise, but draw the wrong conclusion.
The key point is indeed that str_arr is an array in main. While an array decays to a pointer in many (most) expression contexts, this does not apply when a reference to array is initialized. That is the reason why array_size is declared to take a reference to array parameter - this is the only way to have a parameter of array type, which implies that it comes with a defined length.
That is not the case for func. When a function parameter is declared to be of plain array type, the the array to pointer decay is applied to that declaraction. Your declaration of func is equivalent to void func(string * args). Thus args is a pointer, not an array. You could call func as
string str_non_array;
func(&str_non_array);
Because of this, a reference-to-array can't bind to it. And anyways, args has completely lost all information about the size of the array it is pointing to.
You could use the same reference-to-array trick as is used in arraysize: declare func as
template <std::size_t N>
void func(string (&args)[N]);
But this gets impractical to do everywhere (and may lead to code bloat, if applied naively to all array-handling code). The C++ equivalent of an array-with-length as available in other languages is std::vector<string> (for dynamically sized arrays) or std::array<string,N> (for fixed size known at compile time). Note that the latter can cause the same code bloat as mentioned above, so in most cases, std::vector<string> would be the preferred type for array that you need to pass to various functions.
Dmitry is right and I would like to explain it a bit further. The reason its happening is because array is not a First Class citizen in C++ and when passed as parameter it decays to pointer and what you get in called function is a pointer to its first element and size is lost.
You can refer C++ arrays as function arguments to see what alternative options are available.
Do I need to put "&" when I pass a 2D array to a function or 2D arrays automatically do so by reference as 1Ds.
void fnc (int& arr[5][5]) {
}
It will be passed by value if you don't specify pass by reference &.
However arrays decay to pointers, so you're basically passing a pointer by value, meaning the memory it points to will be the same.
In common terms, modifying arr inside the function will modify the original arr (a copy is not created).
Also, 1D arrays also aren't passed "automatically" by reference, it just appears so since they decay to pointers.
If you really want to pass the array by reference it would need to be:
void fnc(int (&arr)[5][5]);
Without the inner parentheses, as Mr Anubis says, you will be attempting to pass an array of references which is unlikely to be helpful.
Normally one would just write
void fnc(int arr[][5]);
(You could write arr[5][5], but the first 5 is ignored which can cause confusion.)
This passes the address of the array, rather than the array itself, which I think is what you are trying to achieve.
You should also consider a vector of vectors or other higher-level data structure; raw arrays have many traps for the unwary.
I have some problems using two dimensional array.
static const int PATTERNS[20][4];
static void init_PATTERN()
{
// problem #1
int (&patterns)[20][4] = const_cast<int[20][4]>(PATTERNS);
...
}
extern void UsePattern(int a, const int** patterns, int patterns_size);
// problem #2
UsePattern(10, PATTERNS, sizeof(PATTERNS)/sizeof(PATTERNS[0]));
In the first statement, I need to cast the const off the two dimensional array PATTERNS. The reason for this is that the init function is called only once, and in the remaining code, PATTERNS is strictly read-only.
In the second statement, I need to pass PATTERNS array to the int** argument. Direct passing resulted a compile error.
I have solved the problem, just about the same time that #Andrey posted the answer. Yes int[][] can't be casted to int**.
It can be casted to int* through &(PATTERNS[0][0]), and the function prototype must be modified with row size (the number of elements in a row). The array can be const_cast away with reference syntax.
Firstly, there's no such thing as cast to array type (or to function type) in C++. Yet this is what you are trying to do. If you want to cast away constness from something, you have to cast to either pointer or reference type. In your case you have a reference on the receiving end of the cast, so the cast itself has to be to reference type as well
int (&patterns)[20][4] = const_cast<int (&)[20][4]>(PATTERNS);
Of course, as Bill already noted, casting away constness from a constant object (and then attempting to modify the object) leads to undefined behavior.
Secondly, a two-dimensional array cannot be passed anywhere as an int ** pointer. If you want to pass your PATTERNS somewhere, you can pass it as const int (&)[20][4], const int (*)[20][4], const int [][4], const int (*)[4] or something similar to that, but not as int **. Do a search on SO and/or read some FAQ on arrays to understand why. This has been explained too many times to repeat it again.
When you declare PATTERNS as const, the compiler may set it up in read-only memory. You can't safely cast away const unless the item was originally declared without const.
I'm guessing that your compiler error was cannot convert 'int (*)[4]' to 'int**' for argument '2' to 'void UsePattern(int, int**, int)'?
AndreyT's answer is perfect. I'd only like to add that I believe you would be better using a class that does the init_PATTERN() work in the constructor and overrides the operator[] to give readonly access to the array elements.
This, of course, assuming you can change the UsePattern function to get a reference to such class instead of a pointer to int array.
C++ Arrays are complicated. You can't just throw them around and expect them to work like in some languages. The only way to initialize an array from another array is to navigate a for loop and copy each item individually. This goes doubly for two-dimensional arrays (meaning you'll need two for loops).
It seems like you're trying to make this more complicated than it needs to be. For instance, if the set of values you will be assigning to PATTERNS will be the same every time you run the program, you can initialize a two dimensional variable like this:
static const int foo[2][3] = {{11,12,13},{21,22,23}};
If the set of values assigned to PATTERNS varies from one execution to the next, then you should probably try to find a different way to approach the problem. I would probably wrap the data in a class, especially if your intention is to use similarly-sized two-dimensional arrays elsewhere in the code.
My teacher in c++ told me that call by reference should only be used if I'm not going to change anything on the arrays inside the function.
I have some really big vectors that I'm passing around in my program. All the vectors will be modified inside the functions. My matrices are of sizes about [256*256][256][50]...
Is there some particular reason not to use call-by reference here?
AFAIK call by reference should be way faster and consume less memory?
Besides all common discussions on when and how to pass by possibly const reference for non-primitive types, arrays are quite special here.
Due to backwards compatibility with C, and there due to your specific problem: arrays can be huge, arrays are never really passed by value in either C or C++. The array will decay into a pointer to the first element, so when you write:
void foo( type array[100] );
The compiler is actually processing:
void foo( type *array );
Regardless of what the size of the array is (two common pitfalls there: trusting that array is an array inside foo and believing that it will be guaranteed to be 100 elements on it.
Now, in C++ you can actually pass arrays by reference, but the reference must be of the concrete type of the array, that includes the size:
void foo_array( type (&array)[100] );
The funny syntax there is telling the compiler that the function will take an array of exactly 100 elements of type type. The advantage there is that the compiler can perform size checking for you:
// assuming 'type' is defined
int main() {
type array0[99];
type array1[100];
foo( array0 ); // compiles, but if size=100 is assumed it will probably break
// equivalent to: foo( &array0[0] )
// foo2( array0 ); // will not compile, size is not 100
foo2( array1 ); // compiles, size is guaranteed to be 100
}
Now, the problem is that your function will only work for an array of exactly 100 elements, and in some cases, you might want to perform the same operation in different array sizes. The two solutions are: template the function in the size of the array which will provide a size-safe implementation for each used size --greater compile time and binary size, the template is compiled for every different size-- or using the pass-by-value syntax, which will make the array decay --not safe in size, that must be passed as extra argument, lesser compile time and binary size. A third option is combining both:
void foo( type *array, int size );
template <size_t N>
void foo( type (&array)[N] ) {
foo( array, N );
}
In this case, while there will be one templated foo for each size, the compiler will most probably inline the call and the generated code would be equivalent to the caller providing the array and size. No extra computations needed and type safety for real arrays.
Now, pass-by-reference is very rarely used with arrays.
My teacher in c++ told me that call by reference should only be used if I'm not going to change anything on the arrays inside the function.
It should be used when you are not changing something inside the function or you change things and want the changes to be reflected to the original array or don't care about the changes to be reflected in the original array.
It shouldn't be used if you don't want your function to change your original array (you need to preserve the original values after the call) and the callee function changes the values of the passed argument.
Your teacher is wrong. If you need to modify arrays, pass by reference is the way to go. If you don't want something modified, pass by const reference.
To prevent accidental changes, use pass-by-const-reference; that way, by default*, the passed-in array can't get changed by the called function.
* Can be overridden with const_cast.
You can pass by reference if:
you won't modify passed object
you want to modify object and don't want to keep old object untouched
When you pass something by reference, then only pointer is passed to function. If you pass whole object then you need to copy it, so it will consume more cpu and memory.
Generally speaking, objects should always be passed by reference. Otherwise a copy of the object will be generated and if the object is substantially big, this will affect performance.
Now if the method or function you are calling does not modify the object, it is a good idea to declare the function as follows:
void some_function(const some_object& o);
This will generate a compile error if you attempt to modify the object's state inside the function body.
Also it should be noted that arrays are always passed by reference.
Hold on a second.. I'm scared at how people are answering this one. Arrays, as far as I remember, are always passed by reference.
void function(int array[])
{
std::cout << array[0] << '\n';
}
// somewhere else..
int array[2] = { 1, 2 };
function(array); // No copy happens here; it is passed by reference
Further, you can't say the array argument is a reference explicitly, as that would be the syntax for creating an array of references (something that's not allowed).
void function(int &array[]) // error here
{ /* ... */ }
So what do you mean?
Further, many are saying that you should only do that if you modify the contents of the array inside the function. Then, what about reference-to-const?
void function(const int arr[])
{
std::cout << arr[0] << '\n';
}
-- edit
Will somebody please point me out how to not pass an array by reference in C++?
-- edit
Oh, so you're talking about vectors. Okay, then the rules of thumb are:
Pass by reference only when you want to modify the contents of the vector.
Pass by reference-to-const whenever you can.
Pass by value only when the object in question is really, really small (like a struct containing an integer, for example), or when it makes sense to (can't think of a case out of the top of my head).
Did I miss something?
-- edit
In the case of plain C arrays, it's a good idea to pass them by reference (like in void function(int (&array)[100])) when you want to ensure that the array has a given definite size.
Thanks, dribeas.
Usually, in introductory courses, they tell you that so you don't accidentally change something you didn't want to.
Like if you passed in userName by reference, and accidentally changed it to mrsbuxley that probably would cause errors, or at the very least be confusing later on.
I don't see any reason why you can't pass by reference. Alternatively you could pass pointers around, but I think pass by reference is better sometimes as it avoids null pointer exceptions.
If your teacher has suggested this as some kind of convention, then feel free to break it if it makes sense to. You can always document this in a comment above the function.
Our house style is to NEVER pass an object by value but to always pass a reference or const reference. Not only do we have data structures that can contain 100s of MB of data and pass by value would be an application killer, but also if we were passing 3D points and vectors by value the our applications would grind to a halt.
It is always a good choice to pass object by reference but we need to be careful and first we have to decide what is our purpose/ purpose of our function?
You have to make a choice here, whether we are gonna only read the data of an object or modify it.
Suppose you got an interface like
void increament_value(int& a);
so in this you can modify value an object which we are passing, but it is a disaster when you passing your sensitive data, you might lose you original data and can not revert it, right?
so c++ provides you a functionality to not to change the value of an object whose reference you are passing to a function, and it is always a good choice to pass a const reference of an object for e.g.,
double get_discounted_amount(const double &amount,double discount){
return (amount*discount/100);
}
This guarantees that your actual value of an object is not gonna change, but again it depends on purpose of your interface whether you wanna change it or only use(read) it