第41页[...]有奇怪的语法,带有函数定义,变量声明没有用左大括号和右大括号.
在该书出版时(1990年),该语法是"traditional"而不是"weird".它是Kernighan&;Richie(K&;R)在The C Programming Language ed.1中定义的语法.在1989年发布用于C的ANSI标准之前,这本书是用于C的de facto标准.毫无疑问,这本书的准备早于C89的出版,对于出版商来说,它在出版时已经过时了,这只是不幸的时机.尽管如此,Prentice Hall也出版了《K&;R;2》.艾德和布莱恩·科尼根编辑了这本书--他们肯定知道吗?此外,前言还声称所有代码都是ANSI C语言,并使用了Microsoft C 5.1.我记得1989年在MS C V6中引入了ANSI C,但我可能错了.5.1于1988年发布--ANSI之前的定稿.他们可能还在委员会里争论这件事;-)版本3.0(1984)是第一个声称与ANSI兼容的版本,但这只是一种猜测,而且只针对当时的早期草案.因此,也许我们可以原谅由于当时语言的变化而造成的混乱.
显然,没有人会废弃庞大的现有代码体,因此实用的编译器会接受任何一种语法--可能是默认情况下.当然,微软更关心的是客户能够构建他们现有的代码,而不是严格执行新标准.
也就是说,本书的主题不是专门的C语言,而是编译实现.也许它仍然有关于编译器实现的相关内容,但您可能需要改编代码示例.
在本例中:
等同于ANSI/ISO C:
void ii_io( int(*open_funct)(),
int(*close_funct)(),
int(*read_funct)() )
{
...
在K&;R ed.1 C中,圆括号中的参数列表仅定义参数names,而types定义在该参数和函数的左大括号之间.函数指针语法可能不像更简单的函数接口那样清晰,例如:
int fn( arg1 ) int arg1 { return arg1 ; }
相当于:
int fn( int arg1 ) { return arg1 ; }
如果您try 编译K&;R ed.1代码,您的编译器可能会发出警告,甚至可能会拒绝它,您将不得不设置正在使用的C标准或使用旧的编译器来直接使用代码.
关于:
if( (fd =!name? STDIN: (*Openp) (name, O_RDONLY | O_BINARY)) != -1)
这在ISO C中仍然是有效的代码,所以实际上是另一个问题.Openp
是在第76行分配open_funct
的某个全局函数指针.如果name
是空指针,则布尔表达式!name
将是true
,在这种情况下,文件描述符fd
采用STDIN
(宏为stdin
流的文件描述符,其为零(在第39页上定义的宏).STDIN
没有在标准库中定义,但是POSIX定义了STDIN_FILENO
(unistd.h).
如果name
不为空,则将所引用的函数Openp
(和open_funct
)的返回值赋给fd
.隐式打开名为name
的文件的函数.
关于Openp
电话有几件事需要注意:
- 首先,显式解引用是可选的--在现代C中肯定是这样,对K&;R C不确定.所以
Openp(name, O_RDONLY | O_BINARY)
也是有效的.
open_funct
和Openp
的类型-即int(*)()
-是一个参数数量未定义的函数,因此可以是指向任何函数的指针,该函数返回具有任何类型的任意数量的参数的int
.因此,不可能判断由open_funct
提供的函数是否具有正确的signature.额外的参数将被忽略,并且类型可能不匹配.POSIX open()
是一个可变函数,允许有和没有mode
参数的两个版本(一种"伪"函数重载).没有参数定义允许这样做,但您可以用更严格的:int (*fn)(char*, int, ...)
替换这样的定义,以匹配POSIX open()
签名,这很可能是本文的目的.