网站首页 > 技术文章 正文
今天在阅读《码出高效》的时候看到了一个问题,我也是第一次知道,而且有点颠覆我的认知。所以做了一些测试,并记录下来。
然而测试的结果发现,好像事实并不如书中说的那么简单。
问题是这样的:
if (true) int x;
for (; ; ) int y;
while (true) int z;
这三句话,能通过编译吗,如果能,会给出 Warning 吗,如果不能,那么错误信息是什么。
书中给出的答案是,不能通过编译,会给出 Declaration not allowed here 编译错误信息,并对此,书中解释为:
单语句在没有加大括号的情况下,声明的变量不可能再被使用,编译器认为没有任何意义。
Every declaration that introduces a name has a scope, in which they can be used.
我做了一些测试,发现事情好像并不是那么简单。
我一开始也是这样认为的。我本来以为,至少应该通过编译,最多是给一个 Warning 说 variable is never used。
我比较好奇,除了 Java,其他语言会不会有同样的编译错误。
首先是是测试 C、C++、Java 是不是都对这句话有相同的结果。
先来看 C,测试环境是 CentOS 7.3 x64,GCC 版本是 gcc version 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC)。
C,不管是 ANSI C 还是 C99,都无法通过编译,直接给出 Error。
error: expected expression before ‘int’
如果加上大括号,那么能够通过编译,遇到大家常见的 Warning。
warning: unused variable ‘x’ [-Wunused-variable]
同样的结果在环境中(我测试环境是 CodeBlocks,Windows)得到了相同的结果。
我相信,CLion 等 IDE 也有一样的结果,如果没有,那么可能是 IDE 帮忙优化了,毕竟有些时候,写 C 代码漏 include 一些头文件,编译和运行的结果也是正确的(IDE 帮你自动补上了)。
原因稍后分析,下面继续测试。
C++。
结果是,编译通过,给出 Warning。
事实上与代码无关,即使是对刚才的 .c 文件,只要使用 g++ 编译器,也是通过的。
再来看 Java。
public class Main {
public static void main(String args[]) {
if (true) int jxtxzzw;
}
}
如果是 Eclipse,直接给出错误。
强行运行,编译失败,无法运行。
Unresolved compilation problem: Syntax error on token “)”, Statement expected after this token at …
如果是 IDEA,会给出编译错误信息。
Declaration not allowed here
Java 的理由容易找:
官方给出的说明是:单语句在没有加大括号的情况下,声明的变量不可能再被使用,编译器认为没有任何意义。
Every declaration that introduces a name has a scope, in which they can be used.
至于为什么 C 就不行呢?难道 C 的语法什么地方说了这是不合法的吗?闻所未闻啊?
但是既然 C 的编译器说不行,那肯定是 C 的哪个标准说了这是不合法的,如果这是真的,那么就不是作用域的问题,而是语法、文法的问题。
换句话说,如果 if (true) {int x;} 是合法的,那为什么 if (true) int x;就不符合语法呢?
我查阅了很多资料(链接见文末),最后找到一个比较信服的答案——确实,根据 C 标准,这就是错的,彻底的语法错误。
首先来澄清一个概念,if 条件句后面,必须跟一个语句。
int x 是一个声明,Declaration。
int x = 10; 是一个定义,Definition。
{int x;} 或者 {int x = 10;} 由于有了 {},是一条语句,Statement。
这是 3 个完全不同的概念,也是绝大多数程序员从一开始就分不清的,而且也始终分不清,从来分不清。
所以,现在的问题的答案就很显然了,C 要求 if 后面是一条语句,然而文章一开始那种写法,是 Declaration,不是 Statement,所以语法就是错的。
这也印证了那句 Error:expected expression,我需要一个表达式。
表达式 Expression,显然是一句语句 Statement。
同时,这与 Eclipse 的报错信息是一致的——if 后面需要一个 Statement。( Statement expected after this token at … )
如果从这个角度考虑,那么,IDEA 的错误提示,Declaration not allowed 就不应该从变量作用域的角度考虑这里的变量声明不合法,而是应该理解成,这是一个 Declaration,而我需要一个 Statement,所以 Declaration 在这里是不被允许的。
这是一个 Declaration,而我需要一个 Statement,所以 Declaration 在这里是不被允许的
至于为什么 g++ 就可以,我想这个问题,可能和 为什么有些 IDE 这么写就可以通过编译,我猜想的是(是的,只是猜想,没有验证过),恐怕是这个编译器,包括绝大多数的 IDE,都自动优化了这个地方,把所有的 if 等语句后面,默认当作套上了大括号在处理,即使是单语句,也假装有了一个大括号作用域,然后再编译的。
所以,有些 gcc 的编译器,也有可能可以,但是从我的测试情况来看,MinGW、Cygwin、Windows、Window 的 Ubuntu 子系统、CentOS 下,gcc 都会给出 error。
I guess the counter-intuitive part of the question is: if this is correct C:if (a == 1) { int b = 10;}then why is this not also correct C?if (a == 1) int b = 10;I mean, a one-line conditional if statement should be fine either with or without braces, right?The answer lies in the grammar of the if statement, as defined by the C standard. The relevant parts of the grammar I’ve quoted below. Succinctly: the int b = 10 line is a declaration, not a statement, and the grammar for the if statement requires a statement after the conditional that it’s testing. But if you enclose the declaration in braces, it becomes a statement and everything’s well.
见:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 以及
https://www.quora.com/What-does-error-expected-expression-before-int-mean-in-C
- 上一篇: 错误档案1:Eclipse自动生成swing窗体代码报错
- 下一篇: JAVA-基础-01
猜你喜欢
- 2024-11-25 Windows平台搭建C/C++开发环境-Eclipse入门
- 2024-11-25 16、开发工具eclipse的安装和使用
- 2024-11-25 50个常见 Java 错误以及如何避免它们
- 2024-11-25 Java程序员常用的快捷键:30个Eclipse键盘快捷键分享
- 2024-11-25 eclipse运行时报错端口被占用处理
- 2024-11-25 能让 Windows 10 运行 Android 应用的 "Project Astoria" 是这么回事
- 2024-11-25 看了必收藏的Eclipse下载安装与配置教程(图文详解)
- 2024-11-25 Win10上跑安卓:微软VS Android模拟器独立版下载
- 2024-11-25 Java EE更名Jakarta EE 无法提供向前兼容性
- 2024-11-25 java中一半是天使一半是魔鬼的Unsafe类详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)