我在理解一些C ++语法与函数指针和函数声明相结合时遇到了问题,即:
通常当我们想要声明一种类型的函数时,我们会创建类似的东西:
typedef void(*functionPtr)(int);
这对我来说没问题。从现在开始,functionPtr是一个代表的类型 指向函数的指针,返回void并以值作为参数获取int。
我们可以按如下方式使用它:
typedef void(*functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr fun = function;
fun(5);
return 0;
}
我们在屏幕上打印5
。
我们有指向函数fun
的指针,我们将一些现有指针指向函数 - function
,我们通过指针执行此函数。凉。
现在正如我在一些书中读到的那样,函数和指向函数的方法在某种程度上是相同的,所以实际上在声明function()
函数之后每次我们说函数我们指的是实函数和指向同一类型的函数,因此在编译后,每条指令都会给出相同的结果(5在屏幕上打印):
int main() {
functionPtr fun = function;
fun(5);
(*fun)(5);
(*function)(5);
function(5);
return 0;
}
所以现在,只要我能想象,那些指向函数和函数的指针几乎是一样的,那么它对我来说就好了。
然后我,如果指向函数和实函数的指针是相同的,那么为什么我不能这样做:
typedef void(functionPtr)(int); //removed *
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr fun = function;
fun(5);
return 0;
}
这给了我以下错误:
prog.cpp:12:14:警告:声明'void fun(int)'有'extern'并初始化 functionPtr fun = function;
因此我理解,由于某种原因,编译器现在理解,有趣的是已经存在的功能。然后我尝试了以下:
int main() {
functionPtr fun;
fun(5);
return 0;
}
我收到了链接错误。我以某种方式理解,因为编译器现在将乐趣视为已经存在的函数,然后由于事实,这种乐趣无法定义,我将得到链接错误。因此我改变了变量的名称:
typedef void(functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr function;
function(5);
return 0;
}
所以现在函数在主阴影全局名称函数中,所以function(5)
在声明functionPtr function;
中使用它工作正常并在屏幕上打印5。
所以现在我很震惊。为什么会这样?同样具有误导性的是,当函数指针被声明为:
typedef void(*functionPtr)(int);
我可以用以下方式创建functionPtr类型的函数:
functionPtr function(int a){
std::cout << a << std::endl;
}
然而,当宣布类似的东西时:
typedef void(functionPtr)(int);
这样做:
functionPtr function(int a){
std::cout << a << std::endl;
}
由编译器解释为函数返回函数。如果是这样,为什么以前的声明(typedef void(functionPtr)(int);
)知道,这是一个返回void的函数而不是函数返回functionPtr?
有人可以帮我解释一下发生在我身下的真正情况吗?
我正在使用启用了C ++ 14选项的g ++ C ++编译器。
答案 0 :(得分:8)
嗯,这有点令人困惑。
函数类型和指向函数类型的指针实际上是两种不同的类型(不再类似于int
和指向int
的指针)。但是,有一个规则,函数类型衰减指向几乎所有上下文中的函数类型。这里衰减松散地意味着转换(类型转换和衰减之间存在差异,但你现在可能对它不感兴趣)。
重要的是,几乎每次使用函数类型时,最终会得到指向函数类型的指针。但请注意 - 几乎每次都不是总是!
如果没有,你会遇到一些情况。
typedef void(functionPtr)(int);
functionPtr fun = function;
此代码尝试将一个函数(不是指针!函数!)复制到另一个函数。但当然,这是不可能的 - 你不能用C ++复制函数。编译器不允许这样做,我不敢相信你编译它(你说你有链接器错误?)
现在,这段代码:
typedef void(functionPtr)(int);
functionPtr function;
function(5);
function
不影响任何事情。编译器知道它不是可以调用的函数指针,只调用原始的function
。
答案 1 :(得分:4)
你的例子中最有趣的是这一个,在这里不使用typedef再现:
void function(int a) { // declaration and definition of 'function'
std::cout << a << std::endl;
}
int main() {
void function(int); // declaration of 'function'
function(5);
}
在C ++的大多数情况下,在本地范围内重新声明function
会影响全局::function
。所以期待链接器错误是有意义的 - main()::function
没有定义吗?
除了功能在这方面特别之外。从[basic.scope.pdel]中的注释:
块范围内的函数声明 块作用域中使用extern说明符的变量声明是指成员的声明 一个封闭的命名空间,但它们不会在该范围内引入新名称。
因此代码示例完全等同于:
void function(int a) { /* ... */ }
void function(int ); // just redeclaring it again, which is ok
int main() {
function(5);
}
您还可以通过将全局function
放入某个命名空间N
来验证这一点。此时,本地范围声明将为::
添加一个名称,但它不会有定义 - 因此您会收到链接器错误。
你提到的另一个有趣的事情是函数到指针转换的概念,[conv.func]:
函数类型T的左值可以转换为“指向T的指针”的prvalue。结果是指向 功能。
当你有一个函数调用表达式时 - 如果你调用的东西是一个函数类型,它首先被转换为一个函数指针。这就是为什么这些是等价的:
fun(5); // OK, call function pointed to by 'fun'
(*fun)(5); // OK, first convert *fun back to 'fun'
function(5); // OK, first convert to pointer to 'function'
(*function)(5); // OK, unary* makes function get converted to a pointer
// which then gets dereferenced back to function-type
// which then gets converted back to a pointer
答案 2 :(得分:3)
让我们逐一看看你的例子,以及它们的真正含义。
typedef void(functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr fun = function;
fun(5);
return 0;
}
在这里,您要为带有functionPtr
的函数创建一个typedef int
,并且不返回值。 functionPtr
实际上不是函数指针的typedef,而是实际函数的函数。
然后,您尝试声明一个新函数fun
,并为其分配function
。遗憾的是,您无法分配功能,因此无效。
int main() {
functionPtr fun;
fun(5);
return 0;
}
同样,您正在使用您指定的签名声明函数fun
。但是你没有定义它,所以你理所当然地没有链接阶段。
typedef void(functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr function;
function(5);
return 0;
}
这里发生了什么?您定义typedef
,并在主要内容中编写functionPtr function;
。这基本上只是您已编写的函数的原型function
。它重申此函数存在,但否则它什么都不做。事实上,你可以写:
typedef void(functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr function;
functionPtr function;
functionPtr function;
void function(int);
function(5);
return 0;
}
你想要多少次,它不会改变一件事。你跟注的function(5)
总是一样的。
你可以用C ++做的一件事就是使用这样的typedef来声明一个函数的原型,但你不能用这种方式来定义它。
typedef void(*functionPtr)(int);
functionPtr function(int a){
std::cout << a << std::endl;
}
这里你定义了一个返回函数指针的函数,但是你没有返回它。根据您的设置,编译器可能会或可能不会抱怨。但function
再次与functionPtr
完全分开。你基本上写了
void (*)(int) function(int a) {
...
}
最后一个例子,你有一个函数返回一个函数的例子,是不允许的,因为它没有意义。
答案 3 :(得分:-1)
typedef void functionPtr (int);
void function (int a){
std::cout << a << std::endl;
}
int main() {
functionPtr *func;
func = function;
func(5);
return 0;
}
你可以用这种方式使用它,我测试过了。 这个问题真的很麻烦。