| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 308697 人关注过本帖, 10 人收藏
标题:关于C标准
只看楼主 加入收藏
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
结帖率:100%
收藏(10)
 问题点数:0 回复次数:50 
关于C标准
1. 前言

本文从英文 C-FAQ (2004 年 7 月 3 日修订版) 翻译而来。本文的 中文版权为朱群英和孙云所有。 本文的内容可以自由用于个人目的,但 是不可以未经许可出版发行。 英文版权为 Steve Summit 所有,详情 见下面的英文版权说明。

The English version of this FAQ list is Copyright 1990-2004 by Steve Summit. Content from the book 《C Programming FAQs: Frequently Asked Questions》 is made available here by permission of the author and the publisher as a service to the community. It is intended to complement the use of the published text and is protected by international copyright laws. The on-line content may be accessed freely for personal use but may not be republished without permission.

最新的 HTML 中译版本可以在 http://c-faq-chn. 取得。 另外在同一地址还提供 PDF 版本的下载。在  http:// 可以得到本文的 LATEX 源文件。

有关英文原文的问题,请咨询 Steve Summit (scs@)。 有关中文译稿的问题, 请联系孙云 (sunyun.s@, 1-12章) 和朱群英 (zhu.qunying@, 13-20章、LATEX文件编辑)。
搜索更多相关主题的帖子: permission Copyright available intended English 
2013-08-11 11:46
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
12.34 我很吃惊, ANSI 标准竟然有那么多没有定义的东西。 标准的唯一任务不就是让这些东西标准化吗?

某些构造随编译器和硬件的实现而变化, 这一直是 C 语言的一个特点。 这种有意的不严格规定可以让编译器生成效率更高的代码, 而不必让所有 程序为了不合理的情况承担额外的负担。因此, 标准只是把现存的实践 整理成文

编程语言标准可以看作是语言使用者和编译器实现者之间的协议。协议的 一部分是编译器实现者同意提供, 用户可以使用的功能。而其它部分则包 括用户同意遵守, 编译器实现者认为会被最受的规则。只要双方都恪守 自己的保证, 程序就可以正确运行。如果任何一方违背它的诺言, 则结果 肯定失败。

参见问题 12.35。

参考资料: [Rationale, Sec. 1.1]。

[ 本帖最后由 TonyDeng 于 2014-7-8 12:21 编辑 ]

授人以渔,不授人以鱼。
2013-08-11 11:49
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
12.35 有人说 i = i++ 的行为是未定义的, 但是我刚在一个兼容 ANSI  的编译器上测试, 得到了我希望的结果。
 
面对未定义行为的时候, 包括范围内的实现定义行为和未确定行为, 编译器 可以做任何实现, 其中也包括你所有期望的结果。但是依靠这个实现却不明智。 参见问题 7.4, 11.31, 11.32 和 11.34。

授人以渔,不授人以鱼。
2013-08-11 11:50
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
12.31 为什么声称兼容 ANSI 的 Frobozz Magic C 编译器不能 编译这些代码?我知道这些代码是 ANSI 的, 因为 gcc 可以编译。

许多编译器都支持一些非标准的扩展, gcc 尤甚。你能确认被拒绝的代码 不依赖这样的扩展吗?通常用试验特定的编译器来确定一种语言的特性是 个坏主意; 使用的标准可能允许变化, 而编译器也可能有错。 参见问题 12.35。

授人以渔,不授人以鱼。
2013-08-11 11:54
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
12.7 我不明白为什么我不能象这样在初始化和数组维度中使用常量: const int n = 5; int a[n];

const 限定词真正的含义是 ``只读的"; 用它限定的对象是运行时 (同常)  不能被赋值的对象。因此用 const 限定的对象的值并 不完全是一个真正的常量。 在这点上 C 和 C++ 不一样。如果你需要真正的运行 时常量, 使用预定义宏 #define (或enum)。

参考资料: [ISO, Sec. 6.4]; [H&S, Secs. 7.11.2,7.11.3 pp. 226-7]。

授人以渔,不授人以鱼。
2013-08-11 11:58
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
12.8 既然不能修改字符串常量, 为什么不把它们定义为字符常量的数组?

一个原因是太多的代码包含
char *p = "Hello, world!";

这样并不正确的语句。这样的语句要受诊断信息的困扰,但真正的问题却出现在 改变 p 所指目的的任何企图。

参见问题 2.13。

授人以渔,不授人以鱼。
2013-08-11 12:01
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
2.13 以下的初始化有什么区别?char a[] = "string literal"; char *p = "string literal"; 当我向 p[i] 赋值的时候, 我的 程序崩溃了。

字符串常量有两种稍有区别的用法。用作数组初始值 (如同在 char a[]  的声明中), 它指明该数组中字符的初始值。其它情况下, 它会转化为一个 无名的静态字符数组, 可能会存储在只读内存中, 这就是造成它不一定能被修改。 在表达式环境中, 数组通常被立即转化为一个指针  (参见第 6 章), 因此第二个声明把 p 初始化成 指向无名数组的第一个元素。

为了编译旧代码, 有的编译器有一个控制字符串是否可写的开关。

参见问题 2.11、 7.1、 7.2 和 7.6。

参考资料: [K&R2, Sec. 5.5 p. 104]; [ISO, Sec. 6.1.4, Sec. 6.5.7]; [Rationale, Sec. 3.1.4]; [H&S, Sec. 2.7.4 pp. 31-2]。


[ 本帖最后由 TonyDeng 于 2013-8-11 12:11 编辑 ]

授人以渔,不授人以鱼。
2013-08-11 12:02
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
7.2 可是我听说 char a[ ] 和 char *a 是一样的。

并非如此。(你所听说的应该跟函数的形式参数有关;参见问题  6.4) 数组不是指针。 数组定义 char a[6] 请求预留 6 个 字符的位置, 并用名称 ``a" 表示。也就是说, 有一个称为 ``a" 的位置, 可以放入 6 个字符。 而指针申明 char *p, 请求一个位置放置一个指 针, 用名称 ``p" 表示。 这个指针几乎可以指向任何位置: 任何字符和 任何连续的字符, 或者哪里也不指(参见问题 5.1 和  1.10)。

一个图形胜过千言万语。声明
    char a[] = "hello";
    char *p = "world";

将会初始化下图所示的数据结果:
       +---+---+---+---+---+---+
    a: | h | e | l | l | o |\0 |
       +---+---+---+---+---+---+
       +-----+     +---+---+---+---+---+---+
    p: |  *======> | w | o | r | l | d |\0 |
       +-----+     +---+---+---+---+---+---+


根据 x 是数组还是指针, 类似 x[3] 这样的引用会生成不同的代码。 认识到这一点大有裨益。以上面的声明为例, 当编译器看到表达式  a[3] 的时候, 它生成代码从 a 的位置开始跳过 3 个, 然后取出那个 字符. 如果它看到 p[3], 它生成代码找到 ``p" 的位置, 取出其中的指 针值, 在指针上加 3 然后取出指向的字符。换言之, a[3] 是 名为 a 的对象 (的起始位置) 之后 3 个位置的值, 而 p[3] 是  p 指向的对象的 3 个位置之后的值. 在上例中, a[3] 和  p[3] 碰巧都是 'l' , 但是编译器到达那里的途径不尽相同。 本质的区别在于类似 a 的数组和类似 p 的指针一旦在表达式中出现就会 按照不同的方法计算, 不论它们是否有下标。下一问题继续深入解释。 参见问题 2.13。

参考资料: [K&R2, Sec. 5.5 p. 104]; [CT&P, Sec. 4.5 pp. 64-5]。


[ 本帖最后由 TonyDeng 于 2013-8-11 12:10 编辑 ]

授人以渔,不授人以鱼。
2013-08-11 12:06
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
12.23 a[3] = "abc"; 合法吗?它是什么意思?

尽管只在极其有限的环境下有用, 可它在 ANSI C (可能也包括一些  ANSI 之前的系统) 中是合法的。它声明了一个长度为 3 的数组, 把它的 三个字符初始化为 'a', 'b' 和 'c', 但却没有通常的 '\0' 字符。因此该数组并不是一个真正的 C 字符串从而不能用在 strcpy, printf %s  等当中。

多数时候, 你应该让编译器计算数组初始化的初始值个数, 在初始值 ``abc" 中, 计算得长度当然应该是 4。

参考资料: [ISO, Sec. 6.5.7]; [H&S, Sec. 4.6.4 p. 98]。

授人以渔,不授人以鱼。
2013-08-11 12:16
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
20.1 怎样从键盘直接读入字符而不用等 RETURN 键?怎样 防止字符输入时的回显?

唉, 在 C 里没有一个标准且可移植的方法。在标准中跟本就 没有提及屏幕和键盘的概念, 只有基于字符 ``流" 的简单输入输出。

在某个级别, 与键盘的交互输入一般上都是由系统取得一行的输入才 提供给需要的程序。这给操作系统提供了一个加入行编辑的机会 (退格、 删除、消除等), 使得系统地操作具一致性, 而不用每一个程序自己 建立。当用户对输入满意, 并键入 RETURN (或等价的键)后, 输入行才被提供 给需要的程序。即使程序中用了读入单个字符的函数 (例如 getchar()  等), 第一次调用就会等到完成了一整行的输入才会返回。这时, 可能 有许多字符提供给了程序, 以后的许多调用 (象 getchar() 的函数)  都会马上返回。

当程序想在一个字符输入时马上读入, 所用的方式途径就采决于行处理在 输入流中的位置, 以及如何使之失效。在一些系统下 (例如 MS-DOS, VMS 的某些模态), 程序可以使用一套不同或修改过的操作系统函数 来扰过行输入模态。在另外一些系统下 (例如 Unix, VMS 的 另一些模态), 操作系统中负责串行输入的部分 (通常称为 ``终端驱动") 必须 设置为行输入关闭的模态, 这样, 所有以后调用的常用输入函数  (例如 read(), getchar() 等) 就会立即返回输入的字符。 最后, 少数的系统 (特别是那些老旧的批处理大型主机) 使用外围处理器 进行输入, 只有行处理模式。

因此, 当你需要用到单字符输入时 (关闭键盘回显也是类似的问题), 你需要 用一个针对所用系统的特定方法, 假如系统提供的话。新闻组 comp.lang.c  讨论的问题基本上都是 C 语言中有明确支持的, 一般上你会从针对个别系统的 新闻组以及相对应的常用问题集中得到更好的解答, 例如  comp.unix.questions 或 comp.os.msdos.programmer。 另外要注意, 有些解答即使是对相似系统的变种也不尽相同, 例如 Unix  的不同变种。同时也要记住, 当回答一些针对特定系统的问题时, 你的答案在你 的系统上可以工作并不代表可以在所有人的系统上都工作。

然而, 这类问题被经常的问起, 这里提供一个对于通常情况的简略回答。

某些版本的 curses 函数库包含了 cbreak(), noecho()  和 getch() 函数, 这些函数可以做到你所需的。如果你只是想要 读入一个简短的口令而不想回显的话, 可以试试 getpass()。在 Unix 系统下, 可以用 ioctl() 来控制终端驱动的模式, ``传统"系统下有 CBREAK 和 RAW 模式, System V 或 POSIX 系统下有 ICANON,  c_cc[VMIN] 和 c_cc[VTIME] 模式, 而 ECHO 模式 在所有系统中都有。必要时, 用函数 system() 和 stty 命令。 更多的信息可以查看所用的系统, 传统系统下, 查看 <sgtty.h>  和 tty(4), System V 下, 查看 <termio.h>  和 termio(4), POSIX 下, 查看 <termios.h>  和 termios(4)。在 MS-DOS 系统下, 用函数 getch() 或  getche(), 或者相对应的 BIOS 中断。在 VMS 下, 使用屏幕管理例程 (SMG$), 或 curses 函数库, 或者低层  $QIO 的 IO$_READVBLK 函数, 以及 IO$M_NOECHO  等其它函数。也可以通过设置 VMS 的终端驱动, 在单字符输入或  ``通过" 模式间切换。 如果是其它操作系统, 你就要靠自己了。

另外需要说明一点, 简单的使用 setbuf() 或 setvbuf() 来设置  sdtin 为无缓冲, 通常并不能切换到单字符输入模式。

如果你在试图写一个可移植的程序, 一个比较好的方法是自己定义三套函数:  1) 设置终端驱动或输入系统进入单字符输入模式, (如果有必要的话),  2) 取得字符, 3) 程序使用结束后的终端驱动复原。理想上, 也许有一天, 这样的一组函数可以成为标准的一部分。本常用问题集的扩充版  (参见问题 20.36) 含有一套适用于几个流行系统的函数。

参见问题 20.2

参考资料: [PCS, Sec. 10 pp. 128-9, Sec. 10.1 pp. 130-1]; [POSIX, Sec. 7]。

授人以渔,不授人以鱼。
2013-08-11 12:20
快速回复:关于C标准
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.019181 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved