åŒæ ·ï¼ŒC ++函数指针。关于语法的困惑

时间:2019-05-06 14:00:13

标签: c++ function-pointers

在this page上,我找到了一个很好的C ++函数指针示例(以åŠå‡½å­ï¼Œä½†æ˜¯è¿™ä¸ªé—®é¢˜ä¸Žå‡½å­æ— å…³ï¼‰ã€‚以下是该页é¢ä¸Šçš„一些副本。

#include <iostream>

double add(double left, double right) {
  return left + right;
}

double multiply(double left, double right) {
  return left * right;
}

double binary_op(double left, double right, double (*f)(double, double)) {
  return (*f)(left, right);
}

int main( ) {
  double a = 5.0;
  double b = 10.0;

  std::cout << "Add: " << binary_op(a, b, add) << std::endl;
  std::cout << "Multiply: " << binary_op(a, b, multiply) << std::endl;

  return 0;
}

我一般地ç†è§£ä»£ç ï¼Œä½†æ˜¯æœ‰äº›äº‹æƒ…我总是感到困惑。函数binary_op()带有一个函数指针*f,但是当使用它时,例如在第19è¡Œbinary_op(a, b, add)上,函数符å·add被传入,而ä¸æ˜¯äººä»¬æƒ³çš„那样&add作为指针。现在您å¯èƒ½ä¼šè¯´è¿™æ˜¯å› ä¸ºç¬¦å·add 是的指针;它是与功能add()相对应的代ç ä½çš„地å€ã€‚很好,但是这里似乎ä»ç„¶å­˜åœ¨ç±»åž‹å·®å¼‚。函数binary_op()使用*f,这æ„味ç€f是指å‘æŸç‰©çš„指针。我传入add,它本身就是代ç çš„指针。 (对å—?)然åŽä¸ºf分é…了add的值,这使fæˆä¸ºä»£ç çš„指针,这æ„味ç€få°±åƒ{{1 }},这æ„味ç€add的调用应åƒf一样,确切地说应该如何调用f(left, right),但是在第12行,它的命å应åƒadd一样,对我æ¥è¯´ä¼¼ä¹Žå¾ˆå¯¹ï¼Œå› ä¸ºå°±åƒç¼–写(*f)(left, right)一样,而(*add)(left, right)ä¸æ˜¯å‡½æ•°ï¼Œå®ƒæ˜¯*add指å‘的代ç çš„第一个字符。 (对å—?)

我知é“用以下内容替æ¢add的原始定义也å¯ä»¥ã€‚

binary_op()

事实上,这对我æ¥è¯´æ„义更大,但是原始语法没有我上é¢è§£é‡Šçš„那样。

那么,为什么在语法上使用double binary_op(double left, double right, double f(double, double)) { return f(left, right); } 而ä¸æ˜¯ä»…仅使用(*f)是正确的?如果符å·f本身就是一个指针,那么短语“函数指针â€æˆ–“函数指针â€åˆ°åº•æ˜¯ä»€ä¹ˆæ„æ€ï¼Ÿå°±ç›®å‰çš„原始代ç è€Œè¨€ï¼Œå½“我们编写func时,double (*f)(double, double)是什么一类?指å‘指针的指针(因为f本身就是指å‘一些代ç çš„指针)?符å·(*f)与add是åŒä¸€ç§äº‹ç‰©ï¼Œè¿˜æ˜¯(*f)是åŒä¸€ç§äº‹ç‰©ï¼Ÿ

现在,如果这一切的答案是“是的,C ++语法很奇怪,åªéœ€è®°ä½å‡½æ•°æŒ‡é’ˆè¯­æ³•ï¼Œä¸è¦è´¨ç–‘它。†,那么我会很ä¸æƒ…愿地接å—它,但是我真的很想对我在这里的错有一个适当的解释。

我已ç»è¯»è¿‡this question,并且我认为我ç†è§£è¿™ä¸€ç‚¹ï¼Œä½†æ˜¯å¹¶æ²¡æœ‰å‘现它有助于解决我的困惑。我还读过this question,它也无济于事,因为它ä¸èƒ½ç›´æŽ¥è§£å†³æˆ‘的类型差异问题。我å¯ä»¥ç»§ç»­é˜…读互è”网上的信æ¯ä¹‹æµ·ï¼Œä»¥æ‰¾åˆ°ç­”案,但是,嘿,那是Stack Overflow的正确选择å—?

3 个答案:

答案 0 :(得分:3)

这是因为C函数指针是特殊的。

首先,表达å¼add将衰å‡ä¸ºä¸€ä¸ªæŒ‡é’ˆã€‚å°±åƒå¯¹æ•°ç»„的引用会衰å‡ä¸ºæŒ‡é’ˆä¸€æ ·ï¼Œå¯¹å‡½æ•°çš„引用也会衰å‡ä¸ºå‡½æ•°çš„指针。

然åŽï¼Œé‚£é‡Œæœ‰å¥‡æ€ªçš„东西:

return (*f)(left, right);
  

那么,为什么在语法上使用(*f)而ä¸æ˜¯ä»…仅使用f是正确的?

两个都有效,您å¯ä»¥è¿™æ ·é‡å†™ä»£ç ï¼š

return f(left, right);

这是因为å–消引用è¿ç®—符将​​返回对函数的引用,并且对函数的引用或函数指针都被视为å¯è°ƒç”¨çš„。

有趣的是,函数引用的衰å‡éžå¸¸å®¹æ˜“,以至于在调用å–消引用è¿ç®—符时它将衰å‡å›žæŒ‡é’ˆï¼Œä»Žè€Œå¯ä»¥æ ¹æ®éœ€è¦å¤šæ¬¡å–消引用函数:

return (*******f)(left, right); // ah! still works

答案 1 :(得分:1)

  

就目å‰çš„原始代ç è€Œè¨€ï¼Œå½“我们写double (*f)(double, double)时,f是什么一类?

f的类型为double (*)(double, double),å³å®ƒæ˜¯æŒ‡å‘类型为double(double,double)的函数的指针。

  

因为(*f)本身就是一个指针

ä¸æ˜¯ã€‚

问:通过指针间接进行æ“作(例如在*f中)时,您会得到什么?答:您会得到一个左值å‚考。例如,给定对象指针int* ptr,表达å¼*ptr的类型为int&,å³å¯¹int的左值引用。

对于函数指针也是如此:当您通过函数指针间接访问时,您将获得指å‘所指å‘函数的左值引用。对于*f,类型为double (&)(double, double),å³å¯¹ç±»åž‹ä¸ºdouble(double,double)的函数的引用。

  

符å·add是和(*f)一样的东西,还是f是å—?

ä¸åˆæ ¼çš„id表达å¼add与*fçš„å«ä¹‰ç›¸åŒï¼Œå³å®ƒæ˜¯ä¸€ä¸ªå·¦å€¼ï¼š

  

标准è‰æ¡ˆ[expr.prim.id.unqual]

     

...如果实体是函数,则表达å¼ä¸ºå·¦å€¼...


  

传入了函数符å·add,而ä¸æ˜¯ä¼ å…¥å®ƒçš„指针&add。现在您å¯èƒ½ä¼šè¯´è¿™æ˜¯å› ä¸ºç¬¦å·add是一个指针;

ä¸ã€‚é‚£ä¸æ˜¯åŽŸå› ã€‚

addä¸æ˜¯æŒ‡é’ˆã€‚它是一个左值。但是函数类型的左值会éšå¼è½¬æ¢ä¸ºæŒ‡é’ˆï¼ˆè¿™ç§°ä¸ºè¡°å‡ï¼‰ï¼š

  

标准è‰æ¡ˆ[转æ¢åŠŸèƒ½]

     

å¯ä»¥å°†å‡½æ•°ç±»åž‹T的左值转æ¢ä¸ºâ€œæŒ‡å‘T的指针â€çš„å‰å€¼ã€‚结果是指å‘该函数的指针。

因此,以下在语义上是等效的:

binary_op(a, b,  add); // implicit function-to-pointer conversion
binary_op(a, b, &add); // explicit use of addressof operator

  

那么,为什么在语法上使用(*f)而ä¸æ˜¯ä»…仅使用f呢?

事实è¯æ˜Žï¼Œè°ƒç”¨å‡½æ•°å·¦å€¼ä¸Žè°ƒç”¨å‡½æ•°æŒ‡é’ˆå…·æœ‰ç›¸åŒçš„语法:

  

标准è‰ç¨¿[expr.call]

     

函数调用是一个åŽç¼€è¡¨è¾¾å¼ï¼ŒåŽè·Ÿæ‹¬å·ï¼Œå…¶ä¸­åŒ…å«å¯èƒ½ä¸ºç©ºçš„逗å·åˆ†éš”çš„åˆå§‹åŒ–程åºå­å¥åˆ—表,这些å­å¥æž„æˆäº†å‡½æ•°çš„å‚数。   åŽç¼€è¡¨è¾¾å¼åº”具有函数类型或函数指针类型。   对于éžæˆå‘˜å‡½æ•°æˆ–é™æ€æˆå‘˜å‡½æ•°çš„调用,åŽç¼€è¡¨è¾¾å¼åº”为引用函数的 lvalue (在这ç§æƒ…况下,函数到指针的标准转æ¢ä¸º[[ conv.func])在åŽç¼€è¡¨è¾¾å¼ä¸Šè¢«æŠ‘制),或具有函数指针类型。

这些都是相åŒçš„函数调用:

add(parameter_list);    // lvalue
(*f)(parameter_list);   // lvalue

(&add)(parameter_list); // pointer
f(parameter_list);      // pointer

P.S。这两个声明是等效的:

double binary_op(double, double, double (*)(double, double))
double binary_op(double, double, double    (double, double))

这是由于以下规则,它与éšå¼è¡°å‡åˆ°å‡½æ•°æŒ‡é’ˆäº’补:

  

标准è‰ç¨¿[dcl.fct]

     

使用以下规则确定函数的类型。   æ¯ä¸ªå‚数(包括功能å‚数包)的类型由其自己的decl-specifier-seq和声明符确定。   确定æ¯ä¸ªå‚数的类型åŽï¼Œå°†â€œ T的数组â€ç±»åž‹çš„任何å‚数或函数类型T的调整为“指å‘T的指针†...

答案 2 :(得分:1)

首先,当编译器确定å‚数的类型时,将指定为函数声明的函数å‚数调整为指å‘该函数的指针。因此,例如下é¢çš„函数声明

void f( void h() );
void f( void ( *h )() );

是等效的,并且声明相åŒçš„一个函数。

请考虑以下演示程åº

#include <iostream>

void f( void h() );
void f( void ( *h )() );

void h() { std::cout << "Hello Ray\n"; }

void f( void h() ) { h(); }

int main()
{
    f( h );
}

从c ++ 17 Standard(11.3.5函数)开始:

  

5使用以下规则确定函数的类型。的   æ¯ä¸ªå‚数的类型(包括功能å‚数包)为   由自己的decl-specifier-seqå’Œå£°æ˜Žç¬¦ç¡®å®šã€‚åŽ Â Â ç¡®å®šæ¯ä¸ªå‚数的类型,“数组â€ç±»åž‹çš„任何å‚æ•°   T或功能类型T调整为“指å‘T的指针â€ã€‚

å¦ä¸€æ–¹é¢ï¼Œæ ¹æ®C ++ 17标准

  

9如果给定å‚数没有å‚数,则å‚数为   以接收函数å¯ä»¥èŽ·å–值的方å¼ä¼ é€’   通过调用va_arg(21.11)æ¥ç¡®å®šå‚数。 [注æ„:本段   ä¸é€‚用于传递给函数å‚数包的å‚数。   在模æ¿å®žä¾‹åŒ–期间扩展功能å‚数包   (17.6.3),因此当   函数模æ¿ä¸“业化实际上是被调用的。 —尾注]   左值到å³å€¼ï¼ˆ7.1),数组到指针(7.2)和函数到指针(7.3)   对å‚数表达å¼æ‰§è¡Œæ ‡å‡†è½¬æ¢

那么这两个声明有什么区别

void f( void h() );
void f( void ( *h )() );

对于第一个声明,您å¯ä»¥åƒåœ¨å‡½æ•°æŒ‡é’ˆçš„typedef中那样考虑函数体内的å‚æ•°h。

typedef void ( *H )();

例如

#include <iostream>

void f( void h() );
void f( void ( *h )() );

void h() { std::cout << "Hello Ray\n"; }


typedef void ( *H )();

void f( H h ) { h(); }

int main()
{
    f( h );
}

æ ¹æ®C ++ 17标准(8.5.1.2函数调用)

  

1函数调用是åŽç¼€è¡¨è¾¾å¼ï¼ŒåŽè·Ÿæ‹¬å·   包å«ä¸€ä¸ªå¯èƒ½ä¸ºç©ºä¸”以逗å·åˆ†éš”的列表   构æˆå‡½æ•°å‚æ•°çš„åˆå§‹åŒ–å­å¥ã€‚   åŽç¼€è¡¨è¾¾å¼åº”具有函数类型或函数指针   输入。

因此,您也å¯ä»¥å°†å‡½æ•°å®šä¹‰ä¸º

void f( void h() ) { ( *h )(); }

甚至喜欢

void f( void h() ) { ( ******h )(); }

因为当将è¿ç®—符*应用于函数å称时,该函数å称会éšå¼è½¬æ¢ä¸ºè¯¥å‡½æ•°çš„å称。