9

这是一个带有一个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 -> Parser -> Semantic Checker -> ... 在这里不起作用。

4

2 回答 2

12

您有效地定义h为:

void h(int(int)) {}

该参数被解释为一个未命名的函数指针,它接受一个int并返回一个int。当您尝试传递42给它时,编译器会抱怨您正在尝试从整数创建函数指针。

我认为您要问的是编译器如何处理(未命名的)函数指针类型及其可能模棱两可的解析。您的问题与C++中最令人头疼的解析有关。

他们在那里决定,只要函数指针类型和另一种解析方式之间存在歧义,就会将其解释为函数指针。他们这样做是因为当您不希望它成为函数指针时,还有其他方法可以消除歧义(例如 - 将其括在括号中,使用 {} 初始化语法等)。

详细了解解析器编写器如何处理此解析,这里是 C11 的词法分析器和语法:http: //quut.com/c/ANSI-C-grammar-l-2011.html在您的示例中,之前typedefx将是一个IDENTIFIER标记,而在之后,它将是一个标记,因为分析器是通过现在是一个类型TYPEDEF_NAME的符号表来通知的。x在这种特殊情况下,解析是明确的。在这种情况下,您似乎所指的“管道反馈”通过符号表发生,其中词法分析器通过在编译过程中影响其输出的更高级别通知上下文。

编辑: OP 发现的 三篇 文章描述了这个问题以及一些 C 解析器/编译器如何很好地解决它。基本上,几乎可以指定只接受/生成合法 C 语法的上下文无关语法 (CFG)。通过引入允许词法分析器适当地区分标识符和类型定义名称的范围查找表,然后是只接受/生成合法 C 的 CFG [更重要的是 LALR(1) 解析器(例如 - yacc 生成)]可以指定语法。

这是一个比 OP 更可怕的例子:

typedef int x;

int main() { x x = 5; return x; }  /* crazily enough this is legal C syntax and a well formed C program */
于 2015-02-28T19:35:27.797 回答
6

引入 typedef 后

typedef int x;

该函数具有以下定义

void h(int( int ) ) {}

也就是说,它的参数被声明为具有int( int )调整为指向函数的指针的函数类型。

您调用提供整数的函数:

h(42);

没有从整数到函数指针的隐式转换。

我看不出有什么问题

似乎经典的管道 Lexer -> Parser -> Semantic Checker -> ... 在这里不起作用。

该参数被替换为 typedef。
x 具有类型的编译器属性。所以它认为记录像

type-specifier h(type-specifier( type-name ) ) {}
于 2015-02-28T19:36:12.513 回答