1

By "normal variable declarations", I assume the declarations like the below.

int a = 3;

If you define a parameter of a function, you will write like this:

void func(int a) {
    <statement>
}

Let's assume a more complicated case. When you normally declare an array of pointer to functions, you will write

int (*p[3])(int, double);

and if you'd like to set this array as a function parameter, you will write

void func(int (*p[])(int, double)) {
    <statement>
}

Now I found that, in any cases, the both ways look almost the same. However, I don't find what kind of "written evidence" assure this rule. In other words, when I write a super complex normal declaration, by what can I believe that I'm able to set the object as a function parameter with the way I just now used in normally declaring the object? As far as I know, this rule is true, but it seems there is no evidence, though I visited so many websites in English and Japanese and even read the C++11 draft.

Does anyone have the evidence?

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
ynn
  • 3,386
  • 2
  • 19
  • 42
  • 1
    Please pick one of C++ or C, the rules differ. – Bathsheba Mar 09 '18 at 15:16
  • @Bathsheba I'm learning both C and C++. But if I have to choose only one, I would like to know about C++. – ynn Mar 09 '18 at 15:18
  • 2
    Declarations including arrays differ in some ways between outside and as function parameter. See e.g. https://stackoverflow.com/questions/8269048/length-of-array-in-function-argument – Yunnosch Mar 09 '18 at 15:19
  • 2
    I know it's an academic question, but please don't ever write code like that. – StoryTeller - Unslander Monica Mar 09 '18 at 15:20
  • 1
    There's actually no such guarantee, because if you write `int array[5]` in main or as a parameter, you get different types because of "array adjustment" of parameters. This is true for both C and C++. – Lundin Mar 09 '18 at 15:21
  • @StoryTeller You don't like the part ``? Could you tell me how I should write? – ynn Mar 09 '18 at 15:24
  • 1
    @ynn - No, it's this monstrosity `void func(int (*p[])(int, double))` that will have you failing my own code reviews. – StoryTeller - Unslander Monica Mar 09 '18 at 15:25
  • @Lundin So I have to "remember" ways of writing rather than reasonably building the code? I wanted a consistent rule. – ynn Mar 09 '18 at 15:26
  • i dont really understand the question. YOu are worried that by a typo, the type of the parameter `p` is different from the type of the variable `p` ? What abou typedefs? – 463035818_is_not_an_ai Mar 09 '18 at 15:31
  • @ynn: it is a consistent rule - array expressions "decay" to pointer expressions unless they're the operands of unary `&` or `sizeof` (in C anyway, I think the rule is similar in C++). – John Bode Mar 09 '18 at 15:32
  • @user463035818 Both of `p` is "an array of pointers to functions whose parameters type are `int` and `double` respectively". (Only the first `p` has information of it's size(=3), but it isn't essential, I think.) – ynn Mar 09 '18 at 15:35
  • @JohnBode Yes. That's true. (In C++, `typeid` operator also regard an array not as a pointer to the first element, I think.) But I wanted a consistent rule which can be applied to all cases of parameter definition. – ynn Mar 09 '18 at 15:38
  • 1
    @ynn: For C, look at [n1570](http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1570.pdf), 6.7.6.3/7. For C++, look at [n4296](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf), 8.3.5/5. – John Bode Mar 09 '18 at 15:48
  • Shall we put the C tag back in? The accepted answer is C. – Bathsheba Mar 09 '18 at 16:13
  • @Bathsheba Can I just add the C tag? Or with removing the C++ tag? – ynn Mar 09 '18 at 16:15
  • @ynn: Dunnit. This is one of those cases where both are appropriate. – Bathsheba Mar 09 '18 at 16:16

2 Answers2

4

Disclaimer This answer was written when C++ was the sole language tag of the question.

When you normally declare an array of pointer to functions, you will write

int (*p[3])(int, double);

No. That is a false premise.

You write it like this:

using My_callback_t = int (int, double);

std::array<My_callback_t*, 3> p;
// or
std::array<std::function<My_callback_t>, 3> p;

Or use std::vector instead of std::array for more flexibility.

The rest of the question becomes moot in my opinion, but for your amusement this is the monstrosity you are asking for:

void func(int (*(&p)[3])(int, double))
{
}

Just in case I wasn't clear enough: Don't write this!!

void func(int (*(&p)[3])(int, double))
                  ^                       p
                 ^                        is a reference
                    ^                     to an array
                     ^                    of 3 elements of type
               ^                          pointer
                        ^                 to function
                         ^~~~~~~~~~~~     receiving 2 params of type int and double
          ^~~                             and returning int
          ^~~~~~~~~~~~~~~~~~~~~~~~~~      Kill. Me. Now!
bolov
  • 72,283
  • 15
  • 145
  • 224
4

C language answer:

Regular variable declarations and parameter declarations are not always identical. This is regulated by the C standard C11 6.7.6.3 "Function declarators", where we can read

The only storage-class specifier that shall occur in a parameter declaration is register.

This alone is a major difference. We cannot have parameters that are static or extern - they can only be auto(default) or register.

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’,

Meaning all parameters that are declared as arrays get adjusted to a pointer to the first element of that array. This is what happens in your example!

int (*p[3])(int, double); or
int (*p[])(int, double); or
int (*p[666])(int, double);

when written as a parameter to a function, all get adjusted ("decay") into a pointer to the first element. That is, a pointer to a function pointer to a function accepting int, double and returning int. Aka this monstrosity: int (**p)(int, double);

You get away with the empty [] just because the compiler doesn't care about the array boundaries. This would actually create an array of incomplete type (which wouldn't be allowed), but the compiler adjusts it to a pointer to the first element instead, so the array size is irrelevant, as that part is lost during "adjustment" anyway.

A declaration of a parameter as ‘‘function returning type’’ shall be adjusted to ‘‘pointer to function returning type’’, as in 6.3.2.1.

Meaning that if you get the crazy idea to pass a whole function to another function, it gets adjusted to a function pointer. Meaning that crazy code like void func( int p (int, double) ) is actually valid and equivalent to void func( int (*p)(int, double) ). This is a special case for function parameters.

Lundin
  • 195,001
  • 40
  • 254
  • 396