关于语言链接,GCC 和 clang 在这种情况下是否显示与 Visual Studio 相同的结果?
Do GCC and clang show the same result as Visual Studio on this case, about language linkage?
Linkage specifications nest. When linkage specifications nest, the
innermost one determines the language linkage. A linkage specification
does not establish a scope. A linkage-specification shall occur only
in namespace scope. In a linkage-specification, the specified
language linkage applies to the function types of all function
declarators, function names with external linkage, and variable
names with external linkage declared within the linkage-specification.
[ Example:
extern "C" // the name f1 and its function type have C language linkage;
void f1(void(*pf)(int)); // pf is a pointer to a C function
...
— end example ]
观察下面传递给函数c_f()
的指针&foo
是而不是指向C函数的指针。这段代码在VS2017中编译链接正常。但它不应该,根据 [dcl.link]/4.
文件 main.cpp
:
#include <stdio.h>
extern "C" // the name c_f and its function type have C language linkage;
void c_f(void(*pf)(int)); // pf is a pointer to a C function
void foo(int i) {
printf("%d\n", i);
}
extern "C" void c_foo(int);
int main() {
c_foo(1); // Calls c_foo(int) defined in other.c
c_f(&foo); // Calls c_f(void(*)(int)) defined in other.c, but &foo is not a pointer to a C function !!
}
文件 other.c
:
#include <stdio.h>
void c_f(void(*pf)(int)){
pf(2);
}
void c_foo(int i) {
printf("%d\n", i);
}
我很想知道 clang 和 GCC 是否符合标准,但我无法在网络编译器中验证这一点。
编辑
我恍然大悟,在上面提到的问题上,我真的不需要两个文件来验证clang和GCC是否符合标准。如果标准需要 C 函数的地址,作为函数 c_f()
的参数,而 main.cpp
中的代码提供 C++ 函数的地址,C++ 编译器不得不抱怨 1 编译此文件时。但这在 clang nor in GCC 中都不会发生。那么我不妨说clang和GCC在这方面也是有bug的
1) 如果我们假设需要诊断
您的代码根据 [dcl.link]/1 and [expr.call]/1(重点是我的)显示未定义的行为:
[dcl.link]/1:
All function types, function names with external linkage, and variable names with external linkage have a language linkage. [ Note: Some of the properties associated with an entity with language linkage are specific to each implementation and are not described here. For example, a particular language linkage may be associated with a particular form of representing names of objects and functions with external linkage, or with a particular calling convention, etc. — end note ] The default language linkage of all function types, function names, and variable names is C++ language linkage. Two function types with different language linkages are distinct types even if they are otherwise identical.
[expr.call]/1:
A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of initializer-clauses which constitute the arguments to the function. The postfix expression shall have function type or function pointer type. For a call to a non-member function or to a static member function, the postfix expression shall be either an lvalue that refers to a function (in which case the function-to-pointer standard conversion is suppressed on the postfix expression), or it shall have function pointer type. Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior ([dcl.link]). For a call to a non-static member function, the postfix expression shall be an implicit ([class.mfct.non-static], [class.static]) or explicit class member access whose id-expression is a function member name, or a pointer-to-member expression selecting a function member; the call is as a member of the class object referred to by the object expression. In the case of an implicit class member access, the implied object is the one pointed to by this. [ Note: A member function call of the form f() is interpreted as (*this).f() (see [class.mfct.non-static]). — end note ] If a function or member function name is used, the name can be overloaded, in which case the appropriate function shall be selected according to the rules in [over.match]. If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider in the dynamic type of the object expression is called; such a call is referred to as a virtual function call. [ Note: The dynamic type is the type of the object referred to by the current value of the object expression. [class.cdtor] describes the behavior of virtual function calls when the object expression refers to an object under construction or destruction. — end note ]
Linkage specifications nest. When linkage specifications nest, the innermost one determines the language linkage. A linkage specification does not establish a scope. A linkage-specification shall occur only in namespace scope. In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification. [ Example:
extern "C" // the name f1 and its function type have C language linkage; void f1(void(*pf)(int)); // pf is a pointer to a C function
...
— end example ]
观察下面传递给函数c_f()
的指针&foo
是而不是指向C函数的指针。这段代码在VS2017中编译链接正常。但它不应该,根据 [dcl.link]/4.
文件 main.cpp
:
#include <stdio.h>
extern "C" // the name c_f and its function type have C language linkage;
void c_f(void(*pf)(int)); // pf is a pointer to a C function
void foo(int i) {
printf("%d\n", i);
}
extern "C" void c_foo(int);
int main() {
c_foo(1); // Calls c_foo(int) defined in other.c
c_f(&foo); // Calls c_f(void(*)(int)) defined in other.c, but &foo is not a pointer to a C function !!
}
文件 other.c
:
#include <stdio.h>
void c_f(void(*pf)(int)){
pf(2);
}
void c_foo(int i) {
printf("%d\n", i);
}
我很想知道 clang 和 GCC 是否符合标准,但我无法在网络编译器中验证这一点。
编辑
我恍然大悟,在上面提到的问题上,我真的不需要两个文件来验证clang和GCC是否符合标准。如果标准需要 C 函数的地址,作为函数 c_f()
的参数,而 main.cpp
中的代码提供 C++ 函数的地址,C++ 编译器不得不抱怨 1 编译此文件时。但这在 clang nor in GCC 中都不会发生。那么我不妨说clang和GCC在这方面也是有bug的
1) 如果我们假设需要诊断
您的代码根据 [dcl.link]/1 and [expr.call]/1(重点是我的)显示未定义的行为:
[dcl.link]/1:
All function types, function names with external linkage, and variable names with external linkage have a language linkage. [ Note: Some of the properties associated with an entity with language linkage are specific to each implementation and are not described here. For example, a particular language linkage may be associated with a particular form of representing names of objects and functions with external linkage, or with a particular calling convention, etc. — end note ] The default language linkage of all function types, function names, and variable names is C++ language linkage. Two function types with different language linkages are distinct types even if they are otherwise identical.
[expr.call]/1:
A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of initializer-clauses which constitute the arguments to the function. The postfix expression shall have function type or function pointer type. For a call to a non-member function or to a static member function, the postfix expression shall be either an lvalue that refers to a function (in which case the function-to-pointer standard conversion is suppressed on the postfix expression), or it shall have function pointer type. Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior ([dcl.link]). For a call to a non-static member function, the postfix expression shall be an implicit ([class.mfct.non-static], [class.static]) or explicit class member access whose id-expression is a function member name, or a pointer-to-member expression selecting a function member; the call is as a member of the class object referred to by the object expression. In the case of an implicit class member access, the implied object is the one pointed to by this. [ Note: A member function call of the form f() is interpreted as (*this).f() (see [class.mfct.non-static]). — end note ] If a function or member function name is used, the name can be overloaded, in which case the appropriate function shall be selected according to the rules in [over.match]. If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider in the dynamic type of the object expression is called; such a call is referred to as a virtual function call. [ Note: The dynamic type is the type of the object referred to by the current value of the object expression. [class.cdtor] describes the behavior of virtual function calls when the object expression refers to an object under construction or destruction. — end note ]