Different value of &array in main() and in function() [duplicate] - c++

This question already has answers here:
Passing Arrays to Function in C++
(5 answers)
What is array to pointer decay?
(11 answers)
Closed 4 months ago.
I'm sorry for my bad English first.
I've encountered a strange problem when coding in C++.
using namespace std;
void Func(int a[2][3])
{
cout <<(int) &a;
}
int main()
{
int a[2][3] =
{
{1,2,3},
{4,5,6}
};
cout << (int)&a << endl;
Func(a);
return 0;
}
I was confused that &a in main() and in function Func() returned different values. And strangely, the difference between them always is 212.
Can anyone explain please? Thank you for your help.
P/s:Thank you all for your answer .My teacher says that C++ doesn't allow passing an array by value, because if the array has 1 million elements, that would decrease the performance a lot only for copying all of them, so he says only pass by reference is allowed. That's what make me think those two &a should be the same. Now I get it, thank you everyone!

Your function declaration
void Func(int a[2][3])
is completely equivalent and interchangeable with:
void Func(int (*a)[3]).
As you can see, you are passing a pointer to an array of three ints by value. Therefore the address of the local function parameter is different from the address of the variable in main, even if they may hold the same value.

You're passing the a argument by value, so each function has its own copy of it (of the pointer, not the array data). The constant offset you're seeing comes from the distance between the stack frames of the two functions, and this is constant.
If you change the function to get a reference to the array (void Func(int (&a)[2][3]) you will get the same value in both cases

The parameter and the local variable are distinct objects and since their lifetimes overlap, they must have distinct memory addresses.

Please read about pass by value and pass by references.
So what happened here is:
you initialised an array in main function. &a will refer to the address of a.
you passed as a pass by value argument to another function. A copy of a is created to be consumed in Func and &a will refer to the memory location of a local to Func.
I hope the concept is clear.

Use the following syntax to pass arrays by (const) reference : const int (&a)[2][3]
#include <iostream>
void func(const int (&a)[2][3])
{
for (const auto& row : a)
{
for(const auto& value : row )
{
std::cout << value << " ";
}
std::cout << "\n";
}
}
int main()
{
int a[2][3] =
{
{1,2,3},
{4,5,6}
};
func(a);
return 0;
}

This is because C rules on how pointers and arrays work are a little weird. You're actually taking the address of a pointer to the array, not the actual address of the array. If you want to get the address to the array you need to take the address of the first element instead:
&a[0]

For starters it is a bad idea to cast an address to the type int like
cout << (int)&a << endl;
you could just write
cout << static_cast<void *>( a ) << endl;
or
cout << static_cast<void *>( &a ) << endl;
Or even like
cout << a << endl;
cout << &a << endl;
though with static_cast the code looks more readable.
The both statements will output the same value: the address of the extent of memory occupied by the array.
In this function call
Func(a);
the array designator is implicitly converted to pointer to its first element of the type int( * )[3].
The value of the pointer expression is assigned to the local variable (parameter) a of the function Func.
The function parameter of the pointer type a and the array a defined in main occupy different extents of memory.
If to rename the function parameter as for example
void Func(int b[2][3])
to distinguish it from the array with the same name defined in main then you may imagine the function call the following way
Func(a);
//...
void Func( /*int b[2][3] */ )
{
int ( *b )[3] = a;
cout << static_cast<void *>( &b );
}
Pay attention to that the function parameter declared as having the array type int[2][3] is implicitly adjusted by the compiler to pointer to the array element type that is int ( * )[3].
So as you can see this statement
cout << static_cast<void *>( &b );
outputs the address of the local variable (parameter) b.
If you want to get the address of the array a within the function Func then you should write
cout << static_cast<void *>( b );
In this case the addresses outputted in main and in the function will coincide because the parameter b stores the address of the first element of the array a.
Here is a demonstration program.
#include <iostream>
void Func( int a[2][3] )
{
std::cout << static_cast< void * >( a ) << '\n';
}
int main()
{
int a[2][3] =
{
{1,2,3},
{4,5,6}
};
std::cout << static_cast<void *>( a ) << '\n';
std::cout << static_cast<void *>( &a ) << '\n';
Func( a );
}
The program output might look like
010FFD08
010FFD08
010FFD08
As you can see the three values are equal each other.
But if you will write in the function Func
std::cout << static_cast< void * >( &a ) << '\n';
^^^^
you will get the address of the local variable (parameter) a of the function. It is evident that this address differs from the address of the extent of memory occupied by the array because for the parameter a there was allocated a separate extent of memory the size of which is equal tp the value of the sizeof( int( * )[3] ) and usually this value is equal to 4 or 8.

Related

C++ - Why i can't use int*p=&arr (arr refers to an array) to init a pointer in C++? [duplicate]

int q[10]={0};
cout << q << endl;
cout << &q << endl;
cout << &q[0] << endl;
output is
0x7fffd4d2f860
0x7fffd4d2f860
0x7fffd4d2f860
Now when i do this->
int *r=q; // allowed
int *r=&q[0] // allowed
int *r=&q // not allowed
Why is the third assignment not allowed when it is essentially the same thing?
If you have an array declared like
T a[N];
where T is some type specifier then a pointer to the array will be declared like
T ( *p )[N] = &a;
A general rule is the following. If you have a multidimensional array (including one-dimensional arrays) like for example
T a[N1][N2][N3];
then this declaration you may rewrite like
T ( a[N1] )[N2][N3];
To get a pointer to the first element of the array just substitute the content in the parentheses the following way
T ( *p )[N2][N3] = a;
If you want to get a pointer to the whole array then rewrite the declaration of the array like
T ( a )[N1][N2][N3];
and make the substitution
T ( *p )[N1][N2][N3] = &a;
Compare this with a declaration of a scalar object and a pointer to it.
For example
T obj;
You may rewrite the declaration like
T ( obj );
Now to get a pointer to the object you can write
T ( *p ) = &obj;
Of course in this case the parentheses are redundant and the above declaration is equivalent to
T *p = &obj;
As for this code snippet
int q[10]={0};
cout << q << endl;
cout << &q << endl;
cout << &q[0] << endl;
and its output
0x7fffd4d2f860
0x7fffd4d2f860
0x7fffd4d2f860
then array designators used in expressions with rare exceptions are converted to pointers to their first elements.
So in fact the two expression q and &q[0] in these statements
cout << q << endl;
cout << &q[0] << endl;
are equivalent. On the other hand, the address of the array itself is the address of the memory extent that the array occupies. And in the beginning of the extent there is the first element of the array. So the three expressions give the same result: the address of the extent of memory occupied by the array.
Why is the third assignment not allowed when it is essentially the same thing?
Because The C++ language has a feature called "type safety". There is a type system that helps you keep the logic of your program sound.
One particular rule is that arbitrary pointer types can not be used to initialise pointers of other, incompatible types. In this case, you have a pointer to type int (.i.e. int*) that you try to initalise with an expression of type pointer to type array of 10 int (i.e. int(*)[10]). One type is not implicitly convertible to the other, hence the program is ill-formed.
Then why does cout print same things in all of the three cases?
Because all of the pointers have the same value. The first byte of the first element of the array is the same byte as the first byte of the entire array.
It just so happens that the stream insertion operator handles all pointer types1 exactly the same, so pointers with same value but different type produce the same output.
1 Pointers to character types are an exception. They are treated entirely differently.
Why can't we assign address of array to pointer?
Actually, we can assign address of an array to a pointer. We just cannot assign address of a array (or any other object for that matter) to a pointer of wrong type. We need a pointer to an array in this case:
int (*r)[10] = &q;
You cannot do the third assignment because &q's type is an int (*)[10], which is incompatible with the type of int* r.
The output of cout << &q does not reveal the type of &q. See this documentation link.
q is a fixed-length array. Specifying q by itself in an expression decays into a pointer to the 1st element of q. Thus, q decays to the same pointer value that &q[0] returns. &q, on the other hand, returns the memory address of the q variable itself, and for an array, its 1st element occupies that same memory address.
There is an operator<<(void*) defined for std::ostream, and void* can accept (almost) ANY type of pointer. Since all three of your cout calls resolve to the same memory address, and there is an operator<< that accepts all three types of pointers, that is why all three calls print the same number.
As for your assignments:
q is an int[10], which decays into an int*, which is why int *r=q; works.
&q[0] dereferences q to access its 1st element, which is an int, and then takes the address of that element, producing an int*, which is why int *r=&q[0]; works.
since q is an int[10], &q is an int(*)[10], which DOES NOT decay into an int*, which is why int *r=&q; does not work. You would have to declare r using the correct type:
int (*r)[10] = &q;
The following code demonstrates the differences between arr, &arr and &arr[0] where arr is an array of integers.
#include <iostream>
#include <string_view>
// Reference: https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/56766138#56766138
// Type finding code start
template <typename T>
constexpr auto type_name()
{
std::string_view name, prefix, suffix;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
prefix = "auto type_name() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name() [with T = ";
suffix = "]";
#elif defined(_MSC_VER)
name = __FUNCSIG__;
prefix = "auto __cdecl type_name<";
suffix = ">(void)";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
// Type finding code end
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
std::cout << "Value: " << arr << "\tType: " << type_name<decltype(arr)>() << std::endl;
std::cout << "Value: " << &arr << "\tType: " << type_name<decltype(&arr)>() << std::endl;
std::cout << "Value: " << &arr[0] << "\tType: " << type_name<decltype(&arr[0])>() << std::endl;
return 0;
}
See it in action.
Sample Output:
Value: 0x7ffcdadd22d0 Type: int [5]
Value: 0x7ffcdadd22d0 Type: int (*)[5]
Value: 0x7ffcdadd22d0 Type: int*
While all three are associated with the same memory location, they are of different types.
Reference: Is it possible to print a variable's type in standard C++?

Address of an array different with the address of the first element?

As I know address of array a is the address of first element of this array.
void func(int a[])
{
cout << "address in func: " << &a << endl;;
cout << "GT: " << &a[0] << endl;
}
int main ()
{
int a[] = {0,1,2,3};
cout << "address in main: " << &a << endl;
cout << "address in main a[0]: " << &a[0] << endl;
func(a);
}
Output:
address in main: 0x7ffef67d6790
address in main a[0]: 0x7ffef67d6790
address in func: 0x7ffef67d6778
GT: 0x7ffef67d6790
Why address of array a in func() difference with address of a[0]?
Why address of array a in func() difference with address of a[0]?
Because you're calling the function func() passing a by value. This means the a inside the function func() is actually a copy of the original decayed pointer that you're passing to func() from main(). And since they're different variables, they have different addresses. Thus by writing cout << &a; you're printing the address of this separate local variable named a.
If you want to print the original address, you should write inside func():
void func(int a[])
{
//---------------------------------v---------->removed the &
cout << "address in func: " << a << endl;;
cout << "GT: " << &a[0] << endl;
}
Demo
address in main: 0x7ffcfb0a4320
address in main a[0]: 0x7ffcfb0a4320
address in func: 0x7ffcfb0a4320
GT: 0x7ffcfb0a4320
As it has been explained in the comments, you're not passing the "same" variable to your function.
When you're calling your function, it makes a local copy within it of your array of int.
In order not to waste memory, and let's say you upgrade your program and now, you're not passing to your function an array of int but a std::vector which is quite big, we have in C++ arguments passed by references (myType & myVar).
This is the same than passing an argument by pointers in C.
Passing something by reference actually creates an ALIAS to the variable passed. So you'd now have 2 ways to act on the variable : the variable itself in your main program, and the alias in your function.
Obviously, if you do not want to modify it in the function, you can pass it by const reference (const myType & myVar) which is the same than in C with const *.
It is a fallacy inherited from C that
void func(int a[])
would pass an array to the function.
While int a[] looks like an array you can't actually pass an array to a function. An array always decays to a pointer and the pointer is actually what you pass to the function. So the above is equivalent to writing
void func(int *a)
and maybe now you can see why &a, the address of the local pointer, and &a[0] the address of the first element of the array are different.

Understanding Passing Address by Reference in C++

I wrote a simple piece of C++ code to pass addresses by reference.
I am passing the address of a variable (say y) and an array (say arr) to a class. Both arr and y will get modified inside the class. I want to have the modified values in my main().
Please find my question in the below piece of code as it is easier that way. Thanks.
#include <iostream>
using namespace std;
class A
{
public:
// Assign values to the array and increment _x.
void increment()
{
(*_x)++;
(*_arr)[0] = 1; // Q1. Is it safe to directly access the array like this.
(*_arr)[1] = 2; // Don't I have to allocate memory to the array ?
(*_arr)[2] = 3;
}
// Get the address of the Variable that is passed in main. x will now have &y2.
A (int* &arr, int* &x):
_x(x)
{
*_arr = arr;
}
private:
int* _x;
int** _arr;
};
int main()
{
int y = 9;
int arr[5];
int *pY = &y;
int *pArr = arr;
A *obj1 = new A(pArr, pY);
// This gives a compile time error. warning: initialization of non-const reference int *&' from rvalue `int *'
// A *obj1 = new A(&y); <-- Q2. Why does this give a Compile Time Error ?
obj1->increment();
cout << "y : " << y << endl;
cout << "[0]: " << arr[0] << "; [1]: " << arr[1] << "; [2]: " << arr[2] << endl;
cout << endl;
return 0;
}
In A::increment() function, I am directly assigning values to the array without
allocating memory. Is it safe to do ? If not, how can I allocate memory so that
I can still get the modified array values in main() ?
Why do I get a compile time error whey I pass &y to A's constructor ?
Thanks in advance.
Question 1
In A::increment() function, I am directly assigning values to the array without allocating memory. Is it safe to do ? If not, how can I allocate memory so that I can still get the modified array values in main() ?
Answer
Yes, it is safe.
Question 2
Why do I get a compile time error whey I pass &y to A's constructor ?
Answer
&y is not an lvalue. Hence, it cannot be used where the argument type is int*&.
Problem in posted code
*_arr = arr;
That is a problem since _arr has not been initialized to point to a valid memory. Using *_arr when _arr has not been initialized causes undefined behavior. You can change the type of _arr to:
int* _arr;
and simplify your code a little bit.
class A
{
public:
// Assign values to the array and increment _x.
void increment()
{
(*_x)++;
_arr[0] = 1; // Q1. Is it safe to directly access the array like this.
_arr[1] = 2; // Don't I have to allocate memory to the array ?
_arr[2] = 3;
}
// Get the address of the Variable that is passed in main. x will now have &y2.
A (int* &arr, int* &x):
_x(x),
_arr(arr)
{
}
private:
int* _x;
int* _arr;
};
without changing anything in main.
This is very rarely what you want; a T** is generally an array of arrays or else a pointer value that you want to modify in the caller’s scope. However, neither seems to be what you’re doing here.
It is safe to modify *_arr[0] if and only if _arr has been initialized to a array of non-const arrays, and (*_arr)[0] if and only if it has been initialized as a pointer to a non-const array. Neither appears to be the case here, but if it is, you probably want to give the array length explicitly.
In this example, &y is a constant. You can’t modify it, so you can’t pass it as a non-const variable. You can declare a pointer int *py = &y; and pass that. But consider whether that’s what you want to do.
By the way, it’s not good style to use identifiers that start with underscores, because by the standard, they’re reserved for the compiler to use.
You should tell us what you are trying to do. In my opinion it's nonsense using raw pointers/arrays and naked new/(missing?) delete in C++ without good reason. I would also like to note that it is not considered good practice using the _prefix for class members. Usually leading _ are used for std implementations. I recommend using m_prefix if you insist on one. And why do you give _arr the type int**? Is is supposed to be a 2D-Array? Additionally, it doesn't really make sense passing a pointer by reference. A pointer is already a pointer, if you know what I mean, just pass the pointer around.
I'm just going to assume that you are doing this to understand manual memory management or pointer arithmetics or - wait, right: Tell us what you are trying to do and why. Nevertheless, I don't understand what you have the class for:
#include <iostream>
void increment(int& x, int *arr, int sz)
{
++x;
for (int i = 0; i != sz; ++i)
{
// this just numbers the values respectively (starting at 1)
arr[i] = i + 1;
}
}
int main()
{
using namespace std;
int y = 9;
const int sz = 5;
int arr[sz];
increment(y, arr, sz);
cout << "y : " << y << '\n'
<< "[0]: " << arr[0] << "; [1]: " << arr[1] << "; [2]: " << arr[2] << "\n\n";
}
To answer your questions:
2. First thing first: I don't see any constructor that only takes one argument.
Read up on "Undefined Behaviour (UB)" starting point: What are all the common undefined behaviours that a C++ programmer should know about?
I can't repeat enough that I don't understand what you are going for and that makes it hard to give solid advice.
I tried fixing your version.. well its still terrible... I highly recommend on reading up on std::array, std::vector. Maybe on pointers, C-Style Arrays and how to pass C-Style Arrays as function arguments (note: for regular C++ programming you wouldn't be doing/using that, usually).
#include <iostream>
class A {
public:
// Assign values to the array and increment m_x.
void increment()
{
++(*m_x);
m_arr[0] = 1;
m_arr[1] = 2;
m_arr[2] = 3;
}
A (int* arr, int* x):
m_x(x), m_arr(arr)
{
}
private:
int* m_x;
int* m_arr;
};
int main()
{
using namespace std;
int y = 9;
int arr[5];
A obj1(arr, &y);
obj1.increment();
cout << "y : " << y << '\n'
<< "[0]: " << arr[0] << "; [1]: " << arr[1] << "; [2]: " << arr[2] << "\n\n";
A obj2(arr, &y);
obj2.increment();
cout << "y : " << y << '\n'
<< "[0]: " << arr[0] << "; [1]: " << arr[1] << "; [2]: " << arr[2] << "\n\n";
}
You should also read up un pointers/references and their differences
I am actually trying to make your programming life easier. Sorry for long answer.
In answer to your first question
In A::increment() function, I am directly assigning values to the
array without allocating memory. Is it safe to do ? If not, how can I
allocate memory so that I can still get the modified array values in
main() ?
you allocated memory in main(), in the line
int arr[5];
In terms of class design, you defined your class constructor to accept reference arguments, which means that an existing int* must be passed to each argument:
A (int* &arr, int* &x)
and you do so when you invoke the constructor:
A *obj1 = new A(pArr, pY);
so in this program, what you are doing is safe. A potential danger if you expect to use this class in another context would be if your arr array in main() contained fewer than 3 elements, since your increment() function initializes the third element of the array.
In answer to your second question
Why do I get a compile time error whey I pass &y to A's constructor ?
In your original constructor,
// Get the address of the Variable that is passed in main. x will now have &y2.
A (int* &arr, int* &x):
_x(x)
{
*_arr = arr;
}
you are dereferencing _arr before it has been initialized. One way to solve this would be to do this:
// Get the address of the Variable that is passed in main. x will now have &y2.
A (int* &arr, int* &x):
_x(x)
{
_arr = new (int*);
*_arr = arr;
}
// Destructor
~A ()
{
delete _arr;
}
As an aside, you also use new in main(). Whenever you use new, you should also use delete to avoid a memory leak. So at the bottom of your program, before the return statement, add the following:
delete obj1;

Why function does not know the array size?

If I write
int main()
{
int a[100] = {1,2,3,4,};
cout<<sizeof(a)/sizeof(a[0])<<endl; //a is a pointer to the first elem of array,
//isn't it
return 0;
}
I get 400!
If I write
void func(int *a);
int main()
{
int a[100] = {1,2,3,4,};
func(a);
return 0;
}
void func(int *a)
{
cout<<sizeof(a)/sizeof(a[0])<<endl; //a is a pointer to the first elem of array
}
Then I get 1!
So why function does not know the array size?
Arrays decay to pointers when passed to functions, so all you will get is the size of the pointer.
sizeof returns the size of the type. In the second example, func( int *a ), a is a pointer and sizeof will report it as such. Even if you did func( int a[100] ), a would be a pointer. If you want the size of the array in func, you must pass it as an extra argument.
This isn't working because sizeof is calculated at compile-time. The function has no information about the size of its parameter (it only knows that it points to a memory address).
Consider using an STL vector instead, or passing in array sizes as parameters to functions.
This was answered by Marcel Guzman in Calculating size of an array!
When passing your array as a parameter to a function taking a pointer, the array decays as a pointer.
void bar(int *a)
{
std::cout << sizeof(a) << std::endl; // outputs "4" (on a 32 bit compiler)
}
void foo()
{
int a[100] ;
std::cout << sizeof(a) << std::endl; // outputs "400" (on a 32 bit compiler)
bar(a);
}
So perhaps the solution is to provide a correct function taking a reference to an array as a parameter :
template <size_t T_Size>
void bar(int (&a)[T_Size])
{
std::cout << T_Size << std::endl; // outputs "100" (on ALL compilers)
std::cout << sizeof(a) << std::endl; // outputs "400" (on a 32 bit compiler)
}
void foo()
{
int a[100] ;
std::cout << sizeof(a) << std::endl; // outputs "400" (on a 32 bit compiler)
bar(a);
}
Of course, the function must be templated.
No. You are wrong.
If I run your second part of code, it gives 1 on my computer. It's not 400.
#include <iostream>
void func(int *a);
using namespace std;
int main()
{
int a[100] = {1,2,3,4,};
func(a);
return 0;
}
void func(int *a)
{
cout<<sizeof(a)/sizeof(a[0])<<endl;
}
Produces
1
You get 400 the first time because you are passing only sizeof(a), not sizeof(a)/sizeof(a[0]), to cout. You need to wrap that calculation with parenthesis to get the correct value outputted, ie:
cout << (sizeof(a)/sizeof(a[0])) << endl;
For the second time, you should be getting 2, 4, or 8 (depending on architecture), definately not 400, since you are essentially outputting this:
cout << sizeof(int*) << endl;
Where the size of a generic pointer is always a fixed value.
A pointer is a pointer. That means, it simply points to memory, and that's all about it. Creating a pointer to an array (which usually means a pointer to the first element of the array, but not necessarily) is still only a pointer to some memory location. As a memory address is simply a memory address there is also no way for the pointer to know that the memory it is pointing to originally was an array, or how long that array was.
It's simply how pointers work. They point to memory, and that's all.
The function does not know the array size in your example because you took explicit steps to convert your array to pointer type, thus completely stripping the function parameter of its original array nature. Once again, you yourself took deliberate steps to make sure that the function does not know the size of the array. Under these circumstances, it is rather strange to see you ask the question about why the function doesn't know the array size.
If you what the function to receive its argument as an array, you have to pass it as an array, not as a mere pointer, and declare the corresponding function parameter accordingly. Arrays in C++ cannot be passed "by value", which means that you'll have to pass it "by reference", as one possibility
void func(int (&a)[100])
{
cout << sizeof a / sizeof a[0] << endl;
}

When a function has a specific-size array parameter, why is it replaced with a pointer?

Given the following program,
#include <iostream>
using namespace std;
void foo( char a[100] )
{
cout << "foo() " << sizeof( a ) << endl;
}
int main()
{
char bar[100] = { 0 };
cout << "main() " << sizeof( bar ) << endl;
foo( bar );
return 0;
}
outputs
main() 100
foo() 4
Why is the array passed as a pointer to the first element?
Is it a heritage from C?
What does the standard say?
Why is the strict type-safety of C++ dropped?
Yes it's inherited from C. The function:
void foo ( char a[100] );
Will have the parameter adjusted to be a pointer, and so becomes:
void foo ( char * a );
If you want that the array type is preserved, you should pass in a reference to the array:
void foo ( char (&a)[100] );
C++ '03 8.3.5/3:
...The type of a function is determined using the following rules. The type of each parameter is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type "array of T" or "function returning T" is adjusted to be "pointer to T" or "pointer to function returning T," respectively....
To explain the syntax:
Check for "right-left" rule in google; I found one description of it here.
It would be applied to this example approximately as follows:
void foo (char (&a)[100]);
Start at identifier 'a'
'a' is a
Move right - we find a ) so we reverse direction looking for the (. As we move left we pass &
'a' is a reference
After the & we reach the opening ( so we reverse again and look right. We now see [100]
'a' is a reference to an array of 100
And we reverse direction again until we reach char:
'a' is a reference to an array of 100 chars
Yes. In C and C++ you cannot pass arrays to functions. That's just the way it is.
Why are you doing plain arrays anyway? Have you looked at boost/std::tr1::array/std::array or std::vector?
Note that you can, however, pass a reference to an array of arbitrary length to a function template. Off the top of my head:
template< std::size_t N >
void f(char (&arr)[N])
{
std::cout << sizeof(arr) << '\n';
}
There is magnificent word in C/C++ terminology that is used for static arrays and function pointers - decay.
Consider the following code:
int intArray[] = {1, 3, 5, 7, 11}; // static array of 5 ints
//...
void f(int a[]) {
// ...
}
// ...
f(intArray); // only pointer to the first array element is passed
int length = sizeof intArray/sizeof(int); // calculate intArray elements quantity (equals 5)
int ptrToIntSize = sizeof(*intArray); // calculate int * size on your system