user-0 提问: C语言标准库函数原型设计

回复:
user: user-1
content: 其实当初，FFFF也就是EOF(-1) 本来就是文件结束标志。 文本文件写入FFFF，表示文件结束。 读文件的时候，读到FFFF表示文件结束了。 所以 getc,getchar 返回 -1 表示文件结束 而 控制台 用^Z或者^D ，表示文件输入结束，只是一个方便的法子

user: user-0
content: 引用 17 楼 paschen 的回复:[quote=引用 13 楼 hijack00的回复:][quote=引用 11 楼 paschen 的回复:] 历史原因，putchar  等一系列函数参数类型都是int  部分认同是历史原因，但还是坚持认为可能有技术层面的考虑。毕竟库的设计者都是大牛，不可能是随随便便就决定了的[/quote] 读下这个：  http://wenku.baidu.com/link?url=1jdn5PU6EurW8Sqpx3VubBJZsTC2OLUWeHCbWq6VEfRahnlYb1XcSKhta-cBnlv9uvAGjPyzbWB38wVxNfcYyw8gtFqGD4cYHNWMwC0sOTG[/quote]  浏览了一下这篇文章，但是其权威性还有待证实。 文中提到 (c = fgetc(fp)) != EOF /* 读到值为FF的字符，误认为 EOF */  按我的理解，fgetc应该是读不到值为FF的字符的。fgetc要么返回一个ASCII字符（这里不考虑读取中文字符的情况），要么是EOF，这样EOF就表示文件末尾或者输入结束。 要想读入类似FF这样的值，我认为应该要用二进制模式打开文件，并且用fread这样的函数来读取文件内容，这样才能将文件内容按照字节进行读取。 而fscanf,fpritnf,fgetc,fputc这一类的函数应该是用于读写文本文件的。

user: user-1
content: 其实，数学运算，大部分，统一为 int,double  是为了编译器，实现的方便 这样，库代码的实现，也简单方便。 同样，参数传递也是一样 为了编译器实现的方便。

user: user-2
content: 引用 13 楼 hijack00的回复:[quote=引用 11 楼 paschen 的回复:] 历史原因，putchar  等一系列函数参数类型都是int  部分认同是历史原因，但还是坚持认为可能有技术层面的考虑。毕竟库的设计者都是大牛，不可能是随随便便就决定了的[/quote] 读下这个：  http://wenku.baidu.com/link?url=1jdn5PU6EurW8Sqpx3VubBJZsTC2OLUWeHCbWq6VEfRahnlYb1XcSKhta-cBnlv9uvAGjPyzbWB38wVxNfcYyw8gtFqGD4cYHNWMwC0sOTG

user: user-1
content: double 类型早就有了，因为fortran 就有 real (对应 float),double 两种不同精度的浮点数了， fortran 是最早的高级语言了  因为当时 double 精度够用了 float 精度不太够用 所以，doule 就作为 浮点类型的代表了 所以标注库的数学函数，都是 double 类型的  至于浮点数协处理器，那是根据 以前浮点数的使用情况，制造出来的 不是因为有了浮点处理器，才有float,double 的 也许 long double 跟浮点处理器有点关系。 float ,double 是早就存在的  浮点数，曾经有个，48Bits 浮点数,这是 pascal最初，常用浮点类型。

user: user-3
content: 有瑕疵也不必回避, 更不用为他披上神圣的光环. 站在现在的观点看过去很多有不完善的地方, 并不代表你回到过去就能做得更好.

user: user-4
content: 特意下了c程序设计语言 可以去看看A.7.3.2 Function Calls 这一节内容。里面讲了这些

user: user-0
content: 引用 11 楼 paschen 的回复:历史原因，putchar  等一系列函数参数类型都是int  部分认同是历史原因，但还是坚持认为可能有技术层面的考虑。毕竟库的设计者都是大牛，不可能是随随便便就决定了的

user: user-3
content: 引用 3 楼 pengzhixi 的回复:你看下EOF的值是-1，你觉得char不能表示么？这方面我觉得更是为了统一。内存对齐这个有点扯了。你传参是从char传过去的，那这个char在哪？不还是在内存么？那么你觉得这个char会对齐么？所以就只是一个算术转换做类型提升而已。而且对齐这个在不同体系结构下标准都不一样。没办法统一的。  EOF 的 -1 和 char 的 -1 是不同的.  如果用 char 的 -1  0xFF 来表示 EOF, 当你读取一个二进制文件时, 难道遇到文件里面的 0XFF 就代表文件结束了? 要表示 EOF 必须用 char 的值范围外的值才行.

user: user-2
content: 历史原因，putchar  等一系列函数参数类型都是int

user: user-5
content: 引用 9 楼 pengzhixi 的回复:[quote=引用 8 楼 user-5 的回复:] [quote=引用 5 楼 pengzhixi 的回复:] 那float提升为double呢？你总不能也说是内存对齐吧。  应该是为了精度吧。  而且，计算及的浮点运算器通常只能处理某一种特定类型的浮点数，一般是 double 或者一种更长的浮点类型。所以在计算的时候，基本还是要转换的。[/quote]现在是可以说为了提高精度和表示范围。但是k&r那个时候应该不是。那个时候是否有专门的浮点运算单元都不一定。所以那个时候应该还说不上为了提高精度。[/quote]  K&R 的时代，C 是没有标准的。K&R 的 The C programming language 是 1978 年出版的，实际是当时 K&R C 的事实标准。  然后刚刚去翻了一下，浮点单元 8087 出现是在 1980 年，IEEE-754 成文是在 1985 年（8087是在IEEE 754 草案阶段实现的），C 的第一版标准是在 1989 年。  在 C&R C 2nd Edition (1988年) 里找到这样一段话： Notice that floats in an expression are not automatically converted to double; this is a change fro the original definition. In general, mathematical functions like those in <math.h> will use double precision. The main reason for using float is to save storage in large arrays, or less often, to save time on machines where double-precision arithmetic is particularly expensive. 所以，当时跟现在还是有很多不一样的。

user: user-4
content: 引用 8 楼 user-5 的回复:[quote=引用 5 楼 pengzhixi 的回复:] 那float提升为double呢？你总不能也说是内存对齐吧。  应该是为了精度吧。  而且，计算及的浮点运算器通常只能处理某一种特定类型的浮点数，一般是 double 或者一种更长的浮点类型。所以在计算的时候，基本还是要转换的。[/quote]现在是可以说为了提高精度和表示范围。但是k&r那个时候应该不是。那个时候是否有专门的浮点运算单元都不一定。所以那个时候应该还说不上为了提高精度。

user: user-5
content: 引用 5 楼 pengzhixi 的回复:那float提升为double呢？你总不能也说是内存对齐吧。  应该是为了精度吧。  而且，计算及的浮点运算器通常只能处理某一种特定类型的浮点数，一般是 double 或者一种更长的浮点类型。所以在计算的时候，基本还是要转换的。

user: user-4
content: 额 类型提升带来的好处就像你说的 可能在某些体系结构上是处于对齐的。访问和操作都会十分高效。至于通过寄存器传参的就不管了。效率怎么都不会太差。

user: user-0
content: 引用 5 楼 pengzhixi 的回复:那float提升为double呢？你总不能也说是内存对齐吧。  我只是以一种不太严谨的说法表达了与你类似的意思。我已经说了我那个说法不严谨了。而且对于ARM直接将参数复制到寄存器的做法，甚至都不涉及到内存。此外，你说的float提升为double类型，或者更复杂的情况（例如实参是结构体等等复合类型）我也考虑到了，所以我上面说的是“大部分都是4字节对齐”，这包括（char,short,int,指针）等等情况。如果真的要钻牛角尖的话，在64位系统上，有可能大部分情况下就是8字节对齐了（我对64位系统了解不多，如果说错了，当我没说）

user: user-4
content: 那float提升为double呢？你总不能也说是内存对齐吧。

user: user-0
content: 引用 3 楼 pengzhixi 的回复:你看下EOF的值是-1，你觉得char不能表示么？这方面我觉得更是为了统一。内存对齐这个有点扯了。你传参是从char传过去的，那这个char在哪？不还是在内存么？那么你觉得这个char会对齐么？所以就只是一个算术转换做类型提升而已。而且对齐这个在不同体系结构下标准都不一样。没办法统一的。  1.（早期）正常的char的类型只有0-127是代表ASCII字符的，最高位固定为0。我是从这个角度去说的，从C语言规范来说，char类型自然是可以表示-1。 2. 我关于内存对齐的说法并不严谨。C语言参数传递在不同体系架构下应该是有区别的，例如，在PC平台下看C语言函数的汇编代码，参数好像都是直接入栈的，而且大部分都是4字节对齐（esp+4,esp+8）。而以前看ARM的时候，好像是说参数比较少的时候直接复制到通用寄存器（两年多没碰了，不知道记得准不准确） 3. 我上面说的内存对齐和你的类型提升其实表达的是差不多的意思。我想表达的意思是：当参数入栈的时候，char类型的实参只有一个字节，通过将其转换为一个int类型再入栈，从而也是4字节，这样就可以对齐了。

user: user-4
content: 你看下EOF的值是-1，你觉得char不能表示么？这方面我觉得更是为了统一。内存对齐这个有点扯了。你传参是从char传过去的，那这个char在哪？不还是在内存么？那么你觉得这个char会对齐么？所以就只是一个算术转换做类型提升而已。而且对齐这个在不同体系结构下标准都不一样。没办法统一的。

user: user-0
content: 引用 1 楼 pengzhixi 的回复:好吧，这个是一个历史原因造成的。因为在k&R那个时期，也就是远古时代。函数的声明是这样的。 char* strchr();  然后定义函数是这样 char*strchr(s,c)  char*s; char c; {  ...  } 你可以看到函数声明是没有参数之类的，不像现在的函数原型 需要有参数类型等信息。这样在传递参数的时候会有个算术转换（现在叫类型提升之类的）就是说凡是比int小的类型 比如char,short传进去之后全部提升为int类型。然后你看到定义里面有char c 这样 又从int转型为char 在函数体里面使用。所以估计后面干脆把类型声明为int也是提醒别人 这里会有个算术转换吧。  标准库中有些函数原型设计是设计者别有用心的，例如getchar返回int而不是char，是为了考虑EOF的情况；再如strcpy等函数返回一个指针是为了实现链式调用，等等。所以我比较好奇除了历史原因，库作者有没有技术上的考虑  另外，你的提醒调用者注意类型转换的说法我也表示同意，即使是char类型，在调用函数时，参数入栈的过程中char类型的实参应该也是存入一个32位寄存器（32位机器上）的，所以声明为int类型可能是出于内存对齐以及容错上的考虑。

user: user-4
content: 引用 25 楼 oN5GrzoN 的回复:[quote=引用 1 楼 pengzhixi 的回复:] 好吧，这个是一个历史原因造成的。因为在k&R那个时期，也就是远古时代。函数的声明是这样的。 char* strchr();  然后定义函数是这样 char*strchr(s,c)  char*s; char c; {  ...  } 你可以看到函数声明是没有参数之类的，不像现在的函数原型 需要有参数类型等信息。这样在传递参数的时候会有个算术转换（现在叫类型提升之类的）就是说凡是比int小的类型 比如char,short传进去之后全部提升为int类型。然后你看到定义里面有char c 这样 又从int转型为char 在函数体里面使用。所以估计后面干脆把类型声明为int也是提醒别人 这里会有个算术转换吧。 翻译的不错呀 http://stackoverflow.com/questions/2394011/why-does-strchr-take-an-int-for-the-char-to-be-found[/quote]是的 照搬上面的。