我遇到了一些有趣的事情,我认为应该是有效的。第一:
编译器/版本
$ gcc --version
gcc (Debian 4.7.2-5) 4.7.2
编译器选项和警告消息。
$ gcc -c warn.c -o warn.o
warn.c:11:5: warning: initialization from incompatible pointer type [enabled by default]
warn.c:11:5: warning: (near initialization for ‘foo.exec’) [enabled by default]
我想知道为什么"Foo()"与"exec"不兼容。(添加评论以希望完全澄清)
typedef struct Thing
{
void (*exec)(char *abc);
} Thing;
// ME: I don't mess with this.. I make const, K?
void Foo(const char *abc)
{
(void) abc;
}
// GCC: LOL, nope! probably u messed up.
Thing foo = { Foo };
const char *
与char *
不同。
来自C标准6.7.5.3(我强调):
15对于要兼容的两种函数类型,两者都应指定兼容的返回类型。此外,参数类型列表(如果两者都存在)的数量应一致参数和省略号终止符的使用相应的参数应具有兼容类型。[…]
来自C标准6.7.5.1(我强调):
2对于兼容的两种指针类型,两者都应具有相同的资格并且两者都应是指向兼容类型的指针。
注:const
是qualifier
基本上,函数void Foo(const char *abc)
表示我将not modify
指向的变量。
但是函子int srtuct void (*exec)(char *abc)
说I might/mightnot modify
是变量。
这是不能接受的。
这只是编译器发现函数签名不匹配并发出警告,而不是错误,因为函数调用在运行时运行良好(函数参数的类型/大小/数量相同)。
警告背后的明显原因是帮助程序员识别函数指针意外被分配给一个函数的情况,该函数同时接受相同的参数,但其操作明显不同。
在当前示例中,编译器可以根据应用于函数参数的
const
修饰符所暗示的意图差异来识别这一点。
基本上,当您定义struct
如下时,
typedef struct Thing
{
void (*exec)(char *abc);
} Thing;
您明确地告诉编译器,函数指针应该只指向与特定签名(参数和返回类型)匹配的函数。
通过如下更新struct
定义,可以很容易地修复此警告
typedef struct Thing
{
void (*exec)(const char *abc);
} Thing;
或者可以通过在所有中不指定参数来完全侧移
typedef struct Thing
{
void (*exec)();
} Thing;
在这种情况下,只要满足返回类型约束,编译器就不会发出警告。
只在这个方向上赋值就可以了,比如
char * a = <whatever>;
const char * b = a;
但在函数签名中,规则比赋值更严格。
函数foo
和函数指针exec
的签名不同。
编译器知道变量是const,根据编译器的不同,编译器可能会进行一些优化。