I got this declaration from https://en.cppreference.com/w/cpp/language/scope, but don't know how to parse this declaration even there is a comment below.
my questions is
how to parse the declaration statement (I see it as a function pointer to a function protocol like "int[3] foo(int n)" or "int foo(int n)[3] --- they are illegal in C++ )? Then, how can I construct a concrete function which can be assigned to this function pointer? Thanks.
const int n = 3;
int (*(*f2)(int n))[n]; // OK: the scope of the function parameter 'n'
// ends at the end of its function declarator
// in the array declarator, global n is in scope
// (this declares a pointer to function returning a pointer to an array of 3 int
It's a pointer to a function taking an int and returning a pointer to an int array of size three.
All the comment is saying is that there are two n identifiers in play here. The [n] (in array declarator) is using the const int 3, not the parameter to the function (which is in the function declarator).
Starting in the middle, with each segment being included in the subsequent bullet point as ...:
f2 is a pointer, (*f2).
It's a pointer to a function taking an integer, ...(int).
It returns a pointer to an int array of size three, int (*...)[3].
You can form a concrete function for it as per the following complete program, which output the first element, 42:
#include <iostream>
const int n = 3;
int (*(*f2)(int n))[n];
int (*g2(int))[n] {
static int x[::n] = { 42 }; // Use outer n, not the parameter.
return &x; // since C++ has no VLAs. This
// means parameter is not actually
// needed in this test case, though
// it may be in more complicated
// tests.
}
int main() {
f2 = &g2; // Assign concrete function to pointer.
auto y = f2(3); // Call via pointer, get array.
std::cout << *(y[0]) << '\n'; // Deref first element to get 42.
}
Having said that, I would be rather curious if one of my colleagues submitting something like that for a code review, at least without a large comment explaining it. Although seasoned developers may be able to work it out, those less experienced may have trouble.
And, in fact, even seasoned developers shouldn't have to work it out, especially given it took me a few minutes.
C++ has a very expressive type system which can easily build something like this up in parts, so you don't have to experience migraines trying to work it out. For something like this, I'd be using std::vector (or std::array) unless there was a compelling case for the added complexity caused by more basic types.
You can create a type for pointer to an array of 3 int
typedef int (*array_with_size_n)[n];
and then use it as return type
const int n = 3;
int (*(*f2)(int n))[n];
int arr[n];
array_with_size_n func(int n)
{
return &arr;
}
int main()
{
f2 = &func;
return 0;
}
Related
I am getting 3 errors:
line 12:-invalid conversion from int* to int
line 17:-x was not declared in this scope
line 16:- expected primary expression before',' token
Please help me to fix this. Here is the code:
#include<iostream>
int power(int x[5])
{
x[0]=12;
x[1]=23;
x[2]=234;
x[3]=344;
x[4]=232;
return x;
}
int main()
{
int action[5]={1,2,3,,4,5};
std::cout<<x[0]<<std::endl;
x();
std::cout<<x[0]<<std::endl;
return 0;
}
your help would be appreciated!
First of all, the errors are pretty self explanatory.
invalid conversion from int* to int
Your function is declared in such a way it is expected to return an int. Not an array of ints, just an int. Furthermore, you cannot return an array from a function. You can, however, return a pointer to the first element, which in your case is unnecessary.
Guessing that you want your function to simply set values in an array, you can achieve that by declaring the function as void returning:
void power(int x[5])
{
x[0]=12;
x[1]=23;
x[2]=234;
x[3]=344;
x[4]=232;
}
x was not declared in this scope
Well, given your main:
int main()
{
int action[5]={1,2,3,,4,5};
std::cout<<x[0]<<std::endl;
// ^
...
}
Here you attempt to use a variable x, which was never declared inside main or as global variable, thus the compiler has no idea what you are referring to. Simply aliasing an argument as x in some unrelated function won't make it visible to all the code. Your can't use it like this.
expected primary expression before',' token
Take a close look at your main function and at action declaration. The part:
int action[5]={1,2,3,,4,5};
// ^^
is illegal. Notice the ,,. You either should put an integer inbetween them, or delete one of them.
What you probably wanted to achieve, was to first declare the array, print out the first element, apply the power() function and print the first element again, hoping it to change. Given the declaration of power() that I have written, you could achieve it by doing it like so:
#include <iostream>
void power(int x[5])
{
x[0]=12;
x[1]=23;
x[2]=234;
x[3]=344;
x[4]=232;
}
int main()
{
int x[5] = {1,2,3,4,5};
std::cout << x[0] << ' ';
power(x);
std::cout << x[0] << std::endl;
}
That outputs: 1 12.
The first error is a simple mis-declaration: The function receives a pointer and returns a pointer, so the return type is int *, not simply int. By the way, I like it that the function returns the pointer because it allows for function nesting like in the code below (the result of a function call can be the direct argument for another).
Note that the parameter x you declare is a pointer, despite appearances. One can declare a pointer parameter in two ways:
f ( int *p );
or
f2( int p[] );
The two are completely identical; it is impossible to pass arrays to functions. They always are "adjusted" to pointers to the first element.
In fact, one could even write
f2( int p[100] );
which I demonstrate in the code below. The number is irrelevant and ignored. In particular, the compiler does not think there is an array, and it does no type or index checking of the actual arguments.
Now the original function you present is unsafe; it's entirely up to the caller to provide an array with 5 elements.
C++ has the possibility to pass references, and even true references to arrays. Arrays of different sizes have different types, even if the elements are of the same type. This makes normal functions taking references to arrays pretty useless (unless you have an application like a screen buffer where all arrays are of the same size, known at coding time).
But C++ has also templates, which are a facility to construct functions at compile time from a provided pattern, depending on type and int parameters provided by the caller at compile time (but not necessarily at template coding time!). For functions these template parameters can be inferred by the compiler from the function arguments which makes template functions handy to use: The compiler creates the proper one almost magically.
A function iterating over all elements of a fixed-size array is a prime candidate for a template. Check out the last function in the code below.
#include<iostream>
using namespace std; // for brevity in the example
/** An unsafe function based on passing pointers
without element counts.
*/
int *power_5_elems(int x[100]) {
// This is somewhat scary.
// There is no way to check whether the array
// starting at x has indeed 5 elements. It's
// like writing assembler: All is up to the caller.
// Another thing: It would be nice to use a loop.
// We know there are 5 elements, right?
x[0] = x[0] * x[0];
x[1] = x[1] * x[1];
x[2] = x[2] * x[2];
x[3] = x[3] * x[3];
x[4] = x[4] * x[4];
return x;
}
// A reference to an int array with 5 elements.
// Remember, a typedef is written like a variable declaration; the
// type name takes the syntactic place of the variable.
// To decipher it, solve the expression in the parentheses first:
// "I must first de-reference the variable".
// The next operator is the square brackets index operator:
// "I must index the result of the dereferencing" (i.e. it is an array).
// There are 5 indices (0..4).
// The last information is: "The resulting type is int."
// Summed up: This is a reference to an array with 5 elements of type int.
typedef int(&intArr5)[5];
/** This is a nicer way to declare the function below. */
intArr5 power_ref5Arr(intArr5 arrRef);
/** A safe function with very limited usability.
It works only for int arrays with 5 elements.
This is the raw declaration of the same function.
*/
int (&power_ref5Arr( int (&x)[5] ))[5] {
// This is not scary at all.
// Only arrays with 5 ints can be supplied.
for (int i = 0; i < 5; i++)
{
x[i] = x[i] * x[i];
}
return x;
}
/** A versatile function which squares arrays
of arbitrary length of any type that isn't on
a tree on the count of three. It's very similar to the
function above with 5 int elements.
*/
template<int N, typename T>
T(&refPower( T (&arr)[N] ))[N]
{
// Note the use of the template parameter N
// in place of the "5" above.
for (int i = 0; i < N; i++)
{
arr[i] = arr[i] * arr[i];
}
return arr;
}
int main() {
int x[5] = { 2,3,4,5,6 };
int *p = &x[0]; // make clear: this is a pointer
power_5_elems(power_5_elems(power_5_elems(x)));
cout << x[0] << endl;
// The function actually expects a pointer!
// The pointer points to x.
power_5_elems(p);
cout << x[0] << endl;
int y[2] = { 2,3 };
refPower(refPower(refPower(y)));
cout << y[0] << endl;
double z[2] = { 0.2 , 0.3 };
refPower(refPower(refPower(z)));
cout << z[0] << endl;
return 0;
}
Say I have the following code:
#include <iostream>
using namespace std;
int defaultvalue[] = {1,2};
int fun(int * arg = defaultvalue)
{
arg[0] += 1;
return arg[0];
}
int main()
{
cout << fun() << endl;
cout << fun() << endl;
return 0;
}
and the result is:
2
3
which make sense because the pointer *arg manipulated the array defaultvalue. However, if I changed the code into:
#include <iostream>
using namespace std;
int defaultvalue[] = {1,2};
int fun(int arg[] = defaultvalue)
{
arg[0] += 1;
return arg[0];
}
int main()
{
cout << fun() << endl;
cout << fun() << endl;
return 0;
}
but the result is still:
2
3
Moreover, when I print out the defaultvalue:
cout << defaultvalue[0] <<endl;
It turn out to be 3.
My question is, in the second example, should the function parameter be passed by value, so that change of arg will have no effect on defaultvalue?
My question is, in the second example, should the function parameter be passed by value, so that change of arg will have no effect on defaultvalue?
No.
It is impossible to pass an array by value (thanks a lot, C!) so, as a "compromise" (read: design failure), int[] in a function parameter list actually means int*. So your two programs are identical. Even writing int[5] or int[24] or int[999] would actually mean int*. Ridiculous, isn't it?!
In C++ we prefer to use std::array for arrays: it's an array wrapper class, which has proper object semantics, including being copyable. You can pass those into a function by value just fine.
Indeed, std::array was primarily introduced for the very purpose of making these silly and surprising native array semantics obsolete.
When we declare a function like this
int func(int* arg);
or this
int (func(int arg[])
They're technically the same. It's a matter of expressiveness. In the first case, it's suggested by the API author that the function should receive a pointer to a single value; whereas in the second case, it suggests that it wants an array (of some unspecified length, possibly ending in nullptr, for instance).
You could've also written
int (func(int arg[3])
which would again be technically identical, only it would hint to the API user that they're supposed to pass in an int array of at least 3 elements. The compiler doesn't enforce any of these added modifiers in these cases.
If you wanted to copy the array into the function (in a non-hacked way), you would first create a copy of it in the calling code, and then pass that one onwards. Or, as a better alternative, use std::array (as suggested by #LightnessRacesinOrbit).
As others have explained, when you put
int arg[] as a function parameter, whatever is inside those brackets doesn't really matter (you could even do int arg[5234234] and it would still work] since it won't change the fact that it's still just a plain int * pointer.
If you really want to make sure a function takes an array[] , its best to pass it like
template<size_t size>
void func (const int (&in_arr)[size])
{
int modifyme_arr[100];
memcpy(modifyme_arr, in_arr, size);
//now you can work on your local copied array
}
int arr[100];
func(arr);
or if you want 100 elements exactly
void func (const int (&arr)[100])
{
}
func(arr);
These are the proper ways to pass a simple array, because it will give you the guaranty that what you are getting is an array, and not just a random int * pointer, which the function doesn't know the size of. Of course you can pass a "count" value, but what if you make a mistake and it's not the right one? then you get buffer overflow.
For example, in a function, "a" array and "first" array (normally arrays cannot take an integer as a parameter which is not const) can take "d" and "a1" as parameter). My question is that why this works like that, what is the difference between them?
#include <iostream>
using namespace std;
class BC {
int first[];
int modify(int a[], int d) {
int a1;
a[a1];
first[a1];
int sec[a1];
//a[d];
//first[d];
//sec[d];
}
};
a[a1]; // This is not an array declaration. This is referring to the a1-th element of the array.
first[a1]; // Same as above
int sec[a1]; // This IS a declaration and so SHOULD fail because a1 is non-const.
To further illuminate, you could say
a[a1] = 5;
or
int x = a[a1];
When you write
a[a1];
you are referring to an element of an array, but without doing anything with the value. This is allowed even though it seems pointless. Potentially, the [] operator can be overidden and hence this statement COULD do something. That's why it's allowed.
I am a bit confused. There are two ways to return an array from a method. The first suggests the following:
typedef int arrT[10];
arrT *func(int i);
However, how do I capture the return which is an int (*)[]?
Another way is through a reference or pointer:
int (*func(int i)[10];
or
int (&func(int i)[10];
The return types are either int (*)[] or int (&)[].
The trouble I am having is how I can assign a variable to accept the point and I continue to get errors such as:
can't convert int* to int (*)[]
Any idea what I am doing wrong or what is lacking in my knowledge?
If you want to return an array by value, put it in a structure.
The Standard committee already did that, and thus you can use std::array<int,10>.
std::array<int,10> func(int i);
std::array<int,10> x = func(77);
This makes it very straightforward to return by reference also:
std::array<int,10>& func2(int i);
std::array<int,10>& y = func2(5);
First, the information you give is incorrect.
You write,
“There are two ways to return an array from a method”
and then you give as examples of the ways
typedef int arrT[10];
arrT *func(int i);
and
int (*func(int i))[10];
(I’ve added the missing right parenthesis), where you say that this latter way, in contrast to the first, is an example of
“through a reference or pointer”
Well, these two declarations mean exactly the same, to wit:
typedef int A[10];
A* fp1( int i ) { return 0; }
int (*fp2( int i ))[10] { return 0; }
int main()
{
int (*p1)[10] = fp1( 100 );
int (*p2)[10] = fp2( 200 );
}
In both cases a pointer to the array is returned, and this pointer is typed as "pointer to array". Dereferencing that pointer yields the array itself, which decays to a pointer to itself again, but now typed as "pointer to item". It’s a pointer to the first item of the array. At the machine code level these two pointers are, in practice, exactly the same. Coming from a Pascal background that confused me for a long time, but the upshot is, since it’s generally impractical to carry the array size along in the type (which precludes dealing with arrays of different runtime sizes), most array handling code deals with the pointer-to-first-item instead of the pointer-to-the-whole-array.
I.e., normally such a low level C language like function would be declared as just
int* func()
return a pointer to the first item of an array of size established at run time.
Now, if you want to return an array by value then you have two choices:
Returning a fixed size array by value: put it in a struct.
The standard already provides a templated class that does this, std::array.
Returning a variable size array by value: use a class that deals with copying.
The standard already provides a templated class that does this, std::vector.
For example,
#include <vector>
using namespace std;
vector<int> foo() { return vector<int>( 10 ); }
int main()
{
vector<int> const v = foo();
// ...
}
This is the most general. Using std::array is more of an optimization for special cases. As a beginner, keep in mind Donald Knuth’s advice: “Premature optimization is the root of all evil.” I.e., just use std::vector unless there is a really really good reason to use std::array.
using arrT10 = int[10]; // Or you can use typedef if you want
arrT10 * func(int i)
{
arrT10 a10;
return &a10;
// int a[10];
// return a; // ERROR: can't convert int* to int (*)[]
}
This will give you a warning because func returns an address of a local variable so we should NEVER code like this but I'm sure this code can help you.
From here: https://stackoverflow.com/a/3473454/499125
int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5
return arr;
}
The definition above can force the compiler to become size-aware of its parameters. This statement seems complex to me. Can someone give an anatomy of this statement or explain how does the statement achieves the size-awareness?
Can this be extended to handle multiple parameters?
It all boils down to being able to read the declaration. A simpler way of writing exactly the same would be:
typedef int int5[5]; // create a typedef
int5& fillarr( int5& ) {
}
As of the exact way of reading the original declaration, just break it into pieces:
int (&fillarr( int (&arr)[5] ))[ 5 ];
^^^^^^^^^^^^^ // A: reference to array of 5 int
^^^^^^ ^^^^^^ // B: reference to array of 5 int
^^^^^^^^ ^ // C: function taking A and returning B
You can split understanding this in two parts, 1) the return value and 2) the parameter type:
int ( &fillarr() )[5] {
static int arr[5];
return arr;
}
We can have a look at the type: int (&fillarr())[5]. To understand this, unfortunately the old C rule: "Declaration is like usage in expressions" doesn't work with references, so let's have a look at a similar declaration instead:
int (*fillarr())[5];
This is a function that returns a pointer instead of a reference to an array of size 5. How do I figure? Well, assume we do have a function f that returns a pointer to an array of ints, how would we access the fifth element? Let's see, we first have to call the function: f(). Then we have to dereference the result *f() and then access the fifth element (which doesn't exist, but ignore that) (*f())[5] which is an int. This is exactly the declaration syntax:
int x;
int (*f())[5];
x = (*f())[5];
// ^^^-------- call f -> yields pointer to array of 5 ints
// ^----------- dereferene the result of f -> yields array of 5 ints
// ^^^---- access the fifth element
Now you only substitute the * for an & because you're returning a reference in your example.
The parameter syntax works analogously.