Ctrl + D是如何与内核文件结束符对应的?如何模拟文件结束符?数字中间为什么不能插入空格或逗号?丰富多彩的语句结束符或分隔符?语句结束符?
目录
Ctrl + D是如何与内核文件结束符对应的?
如何模拟文件结束符?
哪些编程语言支持数值中插入分隔符更容易看清楚?
下划线分隔符
数字中间为什么不能插入空格或逗号?
丰富多彩的语句结束符或分隔符
误用分号
语句结束符
不同语言的结束符
更改语句结束符
Ctrl + D是如何与内核文件结束符对应的?
*nix系统的终端一般会输入Ctrl + D代表输入流结束,Linux和macOS均是如此,尽管Windows用Ctrl + Z,仅仅是外部按键表现,最终目的是一样的。如下以Linux 6.11为例讲解。
- 每开启一个终端会开启一个terminal设备,以tty为例,对应于内核的tty_struct (include\linux\tty.h).
- tty默认配置会定义常见控制按键映射关系:
#define INIT_C_CC { \
[VINTR] = 'C'-0x40, \
[VQUIT] = '\\'-0x40, \
[VERASE] = '\177', \
[VKILL] = 'U'-0x40, \
[VEOF] = 'D'-0x40, \
[VSTART] = 'Q'-0x40, \
[VSTOP] = 'S'-0x40, \
[VSUSP] = 'Z'-0x40, \
[VREPRINT] = 'R'-0x40, \
[VDISCARD] = 'O'-0x40, \
[VWERASE] = 'W'-0x40, \
[VLNEXT] = 'V'-0x40, \
INIT_C_CC_VDSUSP_EXTRA \
[VMIN] = 1 }
上面的VEOF代表文件结束符对应的按键数值是4. - 如上INIT_C_CC会被带入kernel tty标准属性设定 tty_io.c (drivers/tty)
struct ktermios tty_std_termios = { /* for the benefit of tty drivers /
.c_iflag = ICRNL | IXON,
.c_oflag = OPOST | ONLCR,
.c_cflag = B38400 | CS8 | CREAD | HUPCL,
.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN,
.c_cc = INIT_C_CC,
.c_ispeed = 38400,
.c_ospeed = 38400,
/ .c_line = N_TTY, */
}; - 用户层可通过tcgetattr函数获取对应终端tty (struct termios)的c_cc[VEOF]信息,一般而言默认为0x4, 对应于ASCII EOT (End Of Transmission).
- 也可用stty -a命令获取当前tty的设定:
speed 38400 baud; rows 17; columns 198; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; - 综上,Ctrl + D触发按键4,内核根据它注册的EOF传递给上层EOF讯息.
如何模拟文件结束符?
- *nix系统默认可用Ctrl + D按键可给系统发送EOT信号,对应于stdin的EOF.
- Windows对应Ctrl + Z. - *nix系统可用echo命令发送ASCII码4(EOT)来模拟文件结束。
echo -e "\004"
例如,一个程序a.out用getchar读取字符,如下可完成向a.out发送EOF.
echo -e "\004" | ./a.out
- 如上还会额外发送'\n'字符,echo -ne "\004"只会发送EOT.
哪些编程语言支持数值中插入分隔符更容易看清楚?
除了C/ObjC不支持,C++/Java/C#/JS/Swift/VB.NET/Rust/Go/Python 3.6/仓颉 等语言均支持,分隔符一般选择是下划线_, 也有选择用单引号。
- C++:C++14标准引入了对数字字面量中使用单引号'做分隔支持。
当然,用-std=c++11会出现build错误。 - C#:从C# 7.0开始,可以使用下划线在数字字面量中增加可读性。
可通过编译选项/langversion:6.0确认此功能在7.0以上才会支持:error CS8059: 功能“数字分隔符”在 C# 6 中不可用,请使用 7.0 或更高的语言版本。
C# 7.2开始支持十六进制数值和二进制数值(0x开头和0b开头)在0x和0b后面插入下划线。 - Java:从Java 7开始,Java支持在数字字面量中使用下划线。
反向验证,可通过 -target 1.6 -source 1.6选项确认数字字面量存在下划线有编译错误。 - JavaScript 从 ES2021 版本开始支持在数值字面量中使用下划线作为分隔符。
- VB.NET之前不支持,VB.NET最新版已经支持数值中用下划线分隔。例如:
Dim a as Integer = 1_000 - Rust/Swift/Go语言都支持数值中用下划线分隔。
- Python 3.6开始也支持数值用下划线分隔,具体参考:PEP 515 – Underscores in Numeric Literals.
- 仓颉 支持用下划线在数值字面量中,不论是整型还是浮点型。
例如1_23代表123,1.2_3代表1.23.
下划线分隔符
你可能会困惑,C#也允许数值中有多个连续的下划线,其实本质上,编译器在解析数值字面量直接忽略下划线,所以多个连续下划线不影响解析,例如 1__200.
- 需要注意,下划线不能在字面量开头或者结尾。
数字中间为什么不能插入空格或逗号?
整数10000不能写成10 000或者10,000, 原因在于为了简化编译器parser字面量的复杂度。一个标准的编译器词法分析器遇到数值类型会不断获取数字并累积,一旦遇到非数字就会退出。如果允许数字中间加空格或者逗号,词法分析器将更复杂,也容易破坏已有语言的结构,产生冲突。当然,一些高级语言为了提升长数值可读性,可以允许数字中间加入一些分隔符:
- Python (>=3.6版本)可以在数字中间加下划线
number = 1_000_000 - Swift可以在整数、浮点数中间加下划线增强可读性
let number = 1_000_000.000_001
丰富多彩的语句结束符或分隔符
- C/C++/ObjC/Java/C#/Pascal/PHP 等语言用分号分隔。
- Basic/Python用换行分隔。
- SQL语言既可以用换行也可以用分号,在不同语句在同一行时用分号,也支持更改语句分隔符。
- Swift/JavaScript/Go/仓颉/Kotlin 结束符既可用换行,也可用分号。
同一行写多个语句,用分号分隔。 - VB采用换行或者冒号:作为结束符,下划线_作为续行符,VBScript和VB一样。
- VB 2010之后,行续行符逐渐变得可选,即编译器更加智能,并不是直接以换行作为语句的结束,可以更懂程序员的代码。
- 同一行有多个语句,用冒号作为语句分隔符。用下划线续行代表上下行属于同一个语句。
- Rust语句结束符是分号,如果是块语句最后一个语句可以忽略分号。
误用分号
- C语言分号作为语句的结束,如果误用在if、for和while条件语句之后,可能造成提前结束。
例如
if (x == 1);
printf("is 1");
这里if判断这行就已经结束了。幸运的是,GCC提供-Wmisleading-indentation选项检测这样的问题。
if (a < 0)
return
printf("fail");
这里return语句和后面的printf是连在一起作为一个语句。可惜的是,GCC/MSVC打开-Wall也不能提示可能的隐患。 - C/C++ 结构体定义的最后需要有分号,习惯写C#/Java类定义可能忘记这个设定,造成奇怪的编译结果。比如,忘记了分号,结构体定义被当成了一个类型作为返回值。
语句结束符
这个话题听起来很简单,但不同编程语言的不同语法产生了五花八门的语句结束符。大部分编程语言以换行或者分号作为结束符,当然要排除有续行符的情况。
不同语言的结束符
- C/C++/Java/C#用分号分隔。
- Basic/Python用换行分隔。
- SQL语言既可以用换行也可以用分号,在不同语句在同一行时用分号,也支持更改语句分隔符。
- Swift/JavaScript/Go语句既可用换行,也可用分号。
- VB采用换行或者冒号:作为结束符,下划线_作为续行符。VBScript和VB一样。
更改语句结束符
- MySQL提供DELIMITER修改分隔符的方式,但其他SQL语言未必遵循。
若文章对您有帮助,欢迎关注 程序员小迷 。助您在编程路上越走越好!
微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。
我是 程序员小迷 (致力于C、C++、C#、Android、iOS、Java、Kotlin、Objective-C、Swift、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。