百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

《码出高效》学习笔记与书中错误记录

liuian 2024-12-02 22:28 40 浏览

今天在阅读《码出高效》的时候看到了一个问题,我也是第一次知道,而且有点颠覆我的认知。所以做了一些测试,并记录下来。

然而测试的结果发现,好像事实并不如书中说的那么简单。

问题是这样的:

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

相关推荐

eino v0.4.5版本深度解析:接口类型处理优化与错误机制全面升级

近日,eino框架发布了v0.4.5版本,该版本在错误处理、类型安全、流处理机制以及代理配置注释等方面进行了多项优化与修复。本次更新共包含6个提交,涉及10个文件的修改,由2位贡献者共同完成。本文将详...

SpringBoot异常处理_springboot异常注解

在SpringBoot中,异常处理是构建健壮、可维护Web应用的关键部分。良好的异常处理机制可以统一返回格式、提升用户体验、便于调试和监控。以下是SpringBoot中处理异常的完整指...

Jenkins运维之路(Jenkins流水线改造Day02-1-容器项目)

这回对线上容器服务器的流水线进行了一定的改造来满足目前线上的需求,还是会将所有的自动化脚本都放置到代码库中统一管理,我感觉一章不一定写的完,所以先给标题加了个-1,话不多说开干1.本次流水线的流程设计...

告别宕机!零基础搭建服务器监控告警系统!小白也能学会!

前言本文将带你从零开始,一步步搭建一个完整的服务器指标监控与邮件告警系统,使用的技术栈均为业界主流、稳定可靠的开源工具:Prometheus:云原生时代的监控王者,擅长指标采集与告警规则定义Node_...

httprunner实战接口测试笔记,拿走不谢

每天进步一点点,关注我们哦,每天分享测试技术文章本文章出自【码同学软件测试】码同学公众号:自动化软件测试码同学抖音号:小码哥聊软件测试01开始安装跟创建项目pipinstallhttprunne...

基于JMeter的性能压测平台实现_jmeter压测方案

这篇文章已经是两年前写的,短短两年时间,JMeter开源应用技术的发展已经是翻天覆地,最初由github开源项目zyanycall/stressTestPlatform形成的这款测试工具也开始慢...

12K+ Star!新一代的开源持续测试工具!

大家好,我是Java陈序员。在企业软件研发的持续交付流程中,测试环节往往是影响效率的关键瓶颈,用例管理混乱、接口调试复杂、团队协作不畅、与DevOps流程脱节等问题都能影响软件交付。今天,给大家...

Spring Boot3 中分库分表之后如何合并查询

在当今互联网应用飞速发展的时代,数据量呈爆发式增长。对于互联网软件开发人员而言,如何高效管理和查询海量数据成为了一项关键挑战。分库分表技术应运而生,它能有效缓解单库单表数据量过大带来的性能瓶颈。而在...

离线在docker镜像方式部署ragflow0.17.2

经常项目上会出现不能连外网的情况,要怎么使用ragflow镜像部署呢,这里提供详细的步骤。1、下载基础镜像根据docker-compose-base.yml及docker-compose.yml中的i...

看,教你手写一个最简单的SpringBoot Starter

何为Starter?想必大家都使用过SpringBoot,在SpringBoot项目中,使用最多的无非就是各种各样的Starter了。那何为Starter呢?你可以理解为一个可拔插式...

《群星stellaris》军事基地跳出怎么办?解决方法一览

《群星stellaris》军事基地跳出情况有些小伙伴出现过这种情况,究竟该怎么解决呢?玩家“gmjdadk”分享的自己的解决方法,看看能不能解决。我用英文原版、德语、法语和俄语四个版本对比了一下,结果...

数据开发工具dbt手拉手教程-03.定义数据源模型

本章节介绍在dbt项目中,如何定义数据源模型。定义并引入数据源通过Extract和Load方式加载到仓库中的数据,可以使用dbt中的sources组件进行定义和描述。通过在dbt中将这些数据集(表)声...

docker compose 常用命令手册_docker-compose init

以下是DockerCompose常用命令手册,按生命周期管理、服务运维、构建配置、扩缩容、调试工具分类,附带参数解析、示例和关键说明,覆盖多容器编排核心场景:一、生命周期管理(核心命令...

RagFlow与DeepSeek R1本地知识库搭建详细步骤及代码实现

一、环境准备硬件要求独立显卡(建议NVIDIAGPU,8GB显存以上)内存16GB以上,推荐32GB(处理大规模文档时更高效)SSD硬盘(加速文档解析与检索)软件安装bash#必装组件Docker...

Docker Compose 配置更新指南_docker-compose配置

高效管理容器配置变更的最佳实践方法重启范围保留数据卷适用场景docker-composeup-d变更的服务常规配置更新--force-recreate指定/所有服务强制重建down→up流程...