当前位置: 首页 > news >正文

01 词法分析陷阱:C编程中的符号误解

编译器中负责将程序分解为一个一个符号的部分,一般称为“词法分析器”。

本章将探讨:

  • 符号和组成符号的字符间的关系
  • 有关符号含义的一些常见误解

1. =不同于==

一般而言,赋值运算相对于比较运算出现得更频繁,因此字符数较少的符号=就被赋予了更常用的含义—赋值操作。

这样带来了一些好处: 出现频率更高的赋值运算符更加容易的被书写

但是也带来了一个潜在问题: 程序员本意是作比较运算,却可能无意中误写成了赋值运算。

1.1. if (x = y)

if (x = y) break;

带来了一种结果: 只要y不是0, 那么if永远为真.

1.2. while (c = ' ' || c == '\t' || c == '\n')

while (c = ' ' || c == '\t' || c == '\n') // 等价于: c = (' ' || c == '\t' || c == '\n') c = getc (f);

等价于: c = (' ' || c == '\t' || c == '\n')

换言之, while 恒为真 => 死循环

2. &| 不同于&&||

  1. &: 按位与
  2. |: 按位或
  3. &&: 逻辑与
  4. ||: 逻辑或

3. 词法分析中的 "贪心法"

C中会存在由多个字符组成的符号, 因此词法分析的时候每读一个字符, 就需要判断是否与前面的字符共同构成 一个符号?

C编译器词法分析器采用了最简单的方式 -- 如果当前字符与前面字符能够构成一个有效符号, 那就构成, 否则便不共同构成一个符号. 换言之: 每一个符号应该包含尽可能多的字符.

这种词法分析原则本身是没问题的, 但是架不住有些程序员代码书写习惯不良造成代码的含义出现问题.

3.1. a---b

a---b

含义是: a-- - b

3.2. y = x/*p /* p指向除数*/;

y = x/*p       /* p指向除数*/;

程序员 原意 可能为: y = x / (*p)

但是实际上为: y = x

4. 整型常量

如果一个整型常量的第一个字符是数字0,那么该常量将被视作八进制数。

4.1. 10 != 010

int a = 10;
int b = 010;

a的值是10

b的值被当作8进制数解释, 值为8

4.2. 8和9也算八进制数?

有些编译器允许8和9也可以当作八进制数字处理

int a = 0195;

0195的含义是1×8^2+9×8^1+5×8^0,也就是141(十进制)或者0215(八进制)。

我们当然不建议这种用法,ANSI C标准也禁止这种用法。

4.3. 格式对齐可能造成的数字错误

有些时候, 为了格式对齐, 可能无意写成了八进制数字.

struct {int part_number;char *description;
}parttab[] = {046,   "left-handed widget"      ,047,   "right-handed widget"     ,125,    "frammis"
};

5. 字符 和 字符串

C中的''""含义完全不同:

  • '': 代表一个字符常量, 本质是数字,
    eg: 'a'与97等价
  • "": 代表的是一个字符串常量, 其本质是一个指向无名数组的指针, 被指向的无名数组由若干个字符和\0组成.
    eg: printf ("Hello world\n");等价于char hello[] = {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\n', 0}; printf (hello);

5.1. printf('\n');

某些C编译器对函数参数并不进行类型检查,特别是printf函数的参数。

printf('\n'); // error

替代

printf("\n"); // right

可能会出现难以预料的结果.

5.2. 'abcd'

前面说过, 'c'本质是一个整数, 默认是int类型.

整型数(一般为16位或32位)的存储空间可以容纳多个字符(字符一般为8位),因此有的C编译器允许在一个字符常量(以及字符串常量)中包括多个字符.

初学者很容易把

"abcd";

写成:

'abcd'

如果写错了, 结果却恰好符合预期的, 那么完全是你运气好, 因为标准中没有规定应该是什么行为, 这高度依赖编译器的实现.

6. 练习题

6.1. 嵌套注释

某些C编译器允许嵌套注释。请写一个测试程序,要求无论是对允许嵌套注释的编译器,还是对不允许嵌套注释的编译器,该程序都能正常通过编译(无错误消息出现),但是这两种情况下程序执行的结果却不相同。

#include <stdio.h>int main() {int x = 0;/* 外层注释开始x = 1;/* 内层注释 */x = 2;//*/ x = 3;printf("x = %d\n", x);return 0;
}

如果由你来实现一个C编译器,你是否会允许嵌套注释?如果你使用的C编译器允许嵌套注释,你会用到编译器的这一特性吗?你对第二个问题的回答是否会影响到你对第一个问题的回答?

答案:

  • 不允许嵌套注释
  • 不会用到
  • 不会

6.2. 大嘴词法分析器

为什么n-->0的含义是n-- > 0,而不是n- ->0?

答: 词法分析器的"贪心"解析

a+++++b的含义是什么?

a++ + ++b;
http://www.dtcms.com/a/325025.html

相关文章:

  • 深度解析 Spring Boot 循环依赖:原理、源码与解决方案
  • PhotoDirector 安卓版:功能强大的照片编辑与美化应用
  • TypeScript中的type和interface的区别是什么?
  • Shell脚本-数组定义
  • OpenEnler等Linux系统中安装git工具的方法
  • DDR中的POD与ODT
  • 分布式事务原理(高并发系统下的数据一致性保障)
  • 一、线性规划
  • 免费数字人API开发方案
  • 高精度计算+快速幂算法
  • 【算法题】:斐波那契数列
  • 【langchain】如何给langchain提issue和提pull request?
  • SpringIoc 实践和应用--XML配置
  • 数据结构-deque(双端队列)和queue(队列)区别
  • Flask多进程数据库访问问题详解
  • spring全家桶使用教程
  • Lua语言元表、协同程序
  • 密码学的数学基础2-Paillier为什么产生密钥对比RSA慢
  • SQL三剑客:DELETE、TRUNCATE、DROP全解析
  • 深度相机---双目深度相机
  • 浏览器CEFSharp+X86+win7 之 浏览器右键菜单(六)
  • Mysql笔记-存储过程与存储函数
  • vulnhub-doubletrouble靶场攻略
  • Linux C文件操作函数
  • 谷歌DeepMind发布Genie 3:通用型世界模型,可生成前所未有多样化的交互式虚拟环境
  • C++移动语义、完美转发及编译器优化零拷贝
  • PostgreSQL 批量COPY导入优化参数配置
  • 近红外与可见光图像融合的多种方法实现
  • OpenAI正式发布GPT-5:迈向AGI的关键一步
  • Java基础-多线程