这是一个带有int
参数的简单函数:
void f(int x) {}
f(42);
这是另一个带有int
参数的函数:
void g(int(x)) {}
g(42);
现在让我们将x
定义为类型:
typedef int x;
void h(int(x)) {}
h(42);
// warning: passing argument 1 of ‘h’ makes pointer from integer without a cast
(这是我用gcc 4.8.2观察到的行为)
解析器编写者如何处理这种情况?
似乎经典的管道Lexer - >解析器 - >语义检查器 - > ......在这里不起作用。
答案 0 :(得分:12)
您已将h
有效地定义为:
void h(int(int)) {}
该参数被解释为一个未命名的函数指针,它接受int
并返回int
。当您尝试将42
传递给它时,编译器会抱怨您正在尝试从整数中创建函数指针。
我认为你要求的是编译器如何处理(未命名的)函数指针类型及其可能不明确的解析。您的问题与C ++中的the most vexing parse有关。
他们决定,只要函数指针类型和另一种解析方式之间存在歧义,它就会被解释为函数指针。他们这样做是因为当你不希望它成为函数指针时还有其他方法可以消除歧义(例如 - 用括号括起来,使用{}初始化语法等)。
详细了解解析器编写者如何处理此解析,这里是C11的词法分析器和语法:http://quut.com/c/ANSI-C-grammar-l-2011.html在您的示例中,在typedef之前,x
将是之后的IDENTIFIER
令牌,它将是TYPEDEF_NAME
令牌,因为通过符号表通知分析器x
现在是一种类型。在这种特殊情况下,解析是明确的。 "管道反馈"您似乎指的是在这种情况下通过符号表发生,其中词法分析器通过更高级别通知上下文,这会影响其输出随着编译的进行。
编辑:These three articles描述了这个问题以及它是如何通过一些C解析器/编译器很好地解决的。基本上,几乎可以指定仅接受/生成合法C语法的无上下文语法(CFG)。通过引入范围查找表,允许词法分析器适当地区分标识符和typedef名称,然后CFG [更重要的是LALR(1)解析器(例如 - yacc生成)]只接受/生成合法C可以指定语法。
这是一个比OP更为可怕的例子:
typedef int x;
int main() { x x = 5; return x; } /* crazily enough this is legal C syntax and a well formed C program */
答案 1 :(得分:6)
引入typedef后
typedef int x;
该函数具有以下定义
void h(int( int ) ) {}
这是它的参数被声明为具有函数int( int )
的类型,它被调整为指向函数的指针。
您调用提供整数的函数:
h(42);
没有从整数到函数指针的隐式转换。
我认为
没有问题似乎经典的管道Lexer - >解析器 - >语义检查器 - > ......在这里不起作用。
该参数代替typedef。
x具有类型的编译器属性。所以它认为记录像
type-specifier h(type-specifier( type-name ) ) {}