Clang 和 GCC 在定义之前对函数调用的不同诊断行为?
Clang and GCC different diagnostics behaviour for function invocation before definition?
我们来分析下面的代码示例:
int main() {
return func();
}
int func() {
return 1;
}
Clang会报错:
/private/tmp/src.cpp:2,9 - Error - use of undeclared identifier 'func'
但我可以在广泛使用的示例中找到很多此类错误,f.e。一些arduino来源:
https://github.com/firmata/arduino/blob/master/examples/StandardFirmata/StandardFirmata.ino
line 235 : disableI2CPins(); // invocation (before definition, no declaration before)
...
line 651 : void disableI2CPins() { .. } // definition
虽然在定义之前使用了函数,但代码可以用GCC成功编译。怎么可能? Clang 和 GCC 的行为不同吗?是否有任何标志允许 Clang 这样做?
PS。这是编译 Arduino 使用的代码的成功命令行 IDE:
/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avr-g++
-c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10600 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -I/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/cores/arduino
-I/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/variants/mega
-I/Applications/Arduino.app/Contents/Resources/Java/libraries/Servo/src
-I/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/libraries/Wire
-I/Applications/Arduino.app/Contents/Resources/Java/libraries/Firmata/src
/var/folders/64/fwfkm1k51zbd4_c5lwpsbljh0000gn/T/build8049651108713091801.tmp/StandardFirmata.cpp
-o /var/folders/64/fwfkm1k51zbd4_c5lwpsbljh0000gn/T/build8049651108713091801.tmp/StandardFirmata.cpp.o
不,您不希望 clang
在这方面表现得像 gcc
。事实上,使用 -Wall
和 -W
(或 -Wextra
)以获得 gcc
和 clang
两者都会给您更多警告。
But i can find lot's of such errors in wide used examples
可悲的是,要使 C
和 C++
正确非常困难,以至于广泛使用的示例还不够好。
Your example 包含一个可能对该程序没有实际影响的错误。编辑:看来 IDE 足够聪明来解决语言功能。有关详细信息,请参阅@TartanLlama 的回答。也许作者可以声称这是设计使然。但这种情况很少见 clang
庆幸地拯救了你自己。
C89 §3.3.2.2,"Function calls" 包括:
If the expression that precedes the parenthesized argument list in a
function call consists solely of an identifier, and if no declaration
is visible for this identifier, the identifier is implicitly declared
exactly as if, in the innermost block containing the function call,
the declaration
extern int identifier();
appeared
当我第一次了解到这一点时,我心想,"OMG that's not what I wanted at all!" 而且,很多人都同意这个 "feature" 在 C99 中被更改了。
这其实不是编译器的问题,而是Arduino的问题IDE。 IDE 有一个前向声明所有已定义函数的预处理步骤,因此您无需担心函数在文件中的位置。
我的强烈建议是仅遵循语言规则并在使用前前向声明您的函数或定义它们。您的代码将更易于移植和理解。
Clang 在使用 C99 语义的 GNU C11 模式下是默认的。在 C99 中不再允许隐式函数声明。
您可以使用 clang --std=c89
或 clang --std=gnu89
将 clang 置于 c89 模式,这应该允许这种行为,
参见:Are prototypes required for all functions in C89, C90 or C99?
我们来分析下面的代码示例:
int main() {
return func();
}
int func() {
return 1;
}
Clang会报错:
/private/tmp/src.cpp:2,9 - Error - use of undeclared identifier 'func'
但我可以在广泛使用的示例中找到很多此类错误,f.e。一些arduino来源: https://github.com/firmata/arduino/blob/master/examples/StandardFirmata/StandardFirmata.ino
line 235 : disableI2CPins(); // invocation (before definition, no declaration before)
...
line 651 : void disableI2CPins() { .. } // definition
虽然在定义之前使用了函数,但代码可以用GCC成功编译。怎么可能? Clang 和 GCC 的行为不同吗?是否有任何标志允许 Clang 这样做?
PS。这是编译 Arduino 使用的代码的成功命令行 IDE:
/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10600 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -I/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/cores/arduino -I/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/variants/mega -I/Applications/Arduino.app/Contents/Resources/Java/libraries/Servo/src -I/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/avr/libraries/Wire -I/Applications/Arduino.app/Contents/Resources/Java/libraries/Firmata/src /var/folders/64/fwfkm1k51zbd4_c5lwpsbljh0000gn/T/build8049651108713091801.tmp/StandardFirmata.cpp -o /var/folders/64/fwfkm1k51zbd4_c5lwpsbljh0000gn/T/build8049651108713091801.tmp/StandardFirmata.cpp.o
不,您不希望 clang
在这方面表现得像 gcc
。事实上,使用 -Wall
和 -W
(或 -Wextra
)以获得 gcc
和 clang
两者都会给您更多警告。
But i can find lot's of such errors in wide used examples
可悲的是,要使 C
和 C++
正确非常困难,以至于广泛使用的示例还不够好。
Your example 包含一个可能对该程序没有实际影响的错误。编辑:看来 IDE 足够聪明来解决语言功能。有关详细信息,请参阅@TartanLlama 的回答。也许作者可以声称这是设计使然。但这种情况很少见 clang
庆幸地拯救了你自己。
C89 §3.3.2.2,"Function calls" 包括:
If the expression that precedes the parenthesized argument list in a function call consists solely of an identifier, and if no declaration is visible for this identifier, the identifier is implicitly declared exactly as if, in the innermost block containing the function call, the declaration
extern int identifier();
appeared
当我第一次了解到这一点时,我心想,"OMG that's not what I wanted at all!" 而且,很多人都同意这个 "feature" 在 C99 中被更改了。
这其实不是编译器的问题,而是Arduino的问题IDE。 IDE 有一个前向声明所有已定义函数的预处理步骤,因此您无需担心函数在文件中的位置。
我的强烈建议是仅遵循语言规则并在使用前前向声明您的函数或定义它们。您的代码将更易于移植和理解。
Clang 在使用 C99 语义的 GNU C11 模式下是默认的。在 C99 中不再允许隐式函数声明。
您可以使用 clang --std=c89
或 clang --std=gnu89
将 clang 置于 c89 模式,这应该允许这种行为,
参见:Are prototypes required for all functions in C89, C90 or C99?