打印

你必须知道的495个C语言问题

[复制链接]
827|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
engao|  楼主 | 2017-3-31 19:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
如何决定使用那种整数类型?
如果需要大数值 (大于 32, 767 或小于 ¡32, 767), 使用 long 型。否则, 如果空间很重要 (如有大数组或很多结构), 使用 short 型。除此之外, 就使用 int 型。如果严格定义的溢出特征很重要而负值无关紧要, 或者你希望在操作二进制位和字节时避免符号扩展的问题, 请使用对应的无符号类型。但是, 要注意在表达式中混用有符号和无符号值的情况。
尽管字符类型 (尤其是无符号字符型) 可以当成 “小” 整型使用, 但由于不可预知的符号扩展和代码增大有时这样做可能得不偿失。使用无符号字符型有所帮助;
在选择浮点型和双精度浮点型时也有类似的权衡。但如果一个变量的指针必须为特定的类型时, 以上规则不再适用。
如果因为某种原因你需要声明一个有严格大小的变量, 确保象 C99 的 <inttypes.h> 那样用某种适当的 typedef 封装这种选择。通常, 这样做唯一的好原因是试图符合某种外部强加的存储方案。
如果你需要操作超过 C 的内置类型支持的超大变量
沙发
engao|  楼主 | 2017-3-31 21:53 | 只看该作者
64 位机上的 64 位类型是什么样的?
C99 标准定义了 long long 类型, 其长度可以保证至少 64 位, 这种类型在某些编译器上实现已经颇有时日了。其它的编译器则实现了类似 longlong 的扩展。另一方面, 也可以实现 16 位的短整型、32 位的整型和 64 位的长整型, 有些编译器正是这样做的。

使用特权

评论回复
板凳
engao|  楼主 | 2017-3-31 21:54 | 只看该作者
怎样定义和声明全局变量和函数最好?
首先, 尽管一个全局变量或函数可以 (在多个编译单元中) 有多处 “声明”, 但是 “定义” 却只能允许出现一次。定义是分配空间并赋初值 (如果有) 的声明。最好的安排是在某个相关的 .c 文件中定义, 然后在头文件 (.h) 中进行外部声明, 在需要使用的时候, 只要包含对应的头文件即可。定义变量的 .c 文件也应该包含该头文件, 以便编译器检查定义和声明的一致性。
这条规则提供了高度的可移植性: 它和 ANSI C 标准一致, 同时也兼容大多数 ANSI 前的编译器和连接器。Unix 编译器和连接器通常使用 “通用模式” 允许多重定义, 只要保证最多对一处进行初始化就可以了; ANSI C 标准称这种行为为“公共扩展”, 没有语带双关的意思。可以使用预处理技巧来使类似DEFINE(int, i);的语句在一个头文件中只出现一次, 然后根据某个宏的设定在需要的时候转化成定义或声明。但不清楚这样的麻烦是否值得。
如果希望让编译器检查声明的一致性, 一定要把全局声明放到头文件中。特别是, 永远不要把外部函数的原型放到 .c 文件中: 通常它与定义的一致性不能得到检查, 而矛盾的原型比不用还糟糕。

使用特权

评论回复
地板
engao|  楼主 | 2017-3-31 21:56 | 只看该作者
extern 在函数声明中是什么意思?
它可以用作一种格式上的提示表明函数的定义可能在另一个源文件中, 但在extern int f();和int f();
之间并没有实质的区别。

使用特权

评论回复
5
engao|  楼主 | 2017-3-31 21:56 | 只看该作者
关键字 auto 到底有什么用途?
毫无用途;它已经过时

使用特权

评论回复
6
engao|  楼主 | 2017-3-31 21:57 | 只看该作者
我 似 乎 不 能 成 功 定 义一个 链 表 。 我 试 过 typedef struct { char
*item; NODEPTR next; } *NODEPTR; 但是编译器报了错误
信息。难道在C语言中一个结构不能包含指向自己的指针吗?
C 语言中的结构当然可以包含指向自己的指针; [K&R2, 第 6.5 节] 的讨论和
例子表明了这点。 NODEPTR 例子的问题是在声明 next 域的时候 typedef 还没
有定义。为了解决这个问题, 首先赋予这个结构一个标签 (“struct node”)。然后,
声明 “next” 域为 “struct node *”, 或者分开 typedef 定义和结构定义, 或者两者都
采纳。以下是一个修改后的版本:
struct node {
char *item;
struct node *next;
};
typedef struct node *NODEPTR;
至少还有三种同样正确的方法解决这个问题。
在用 typedef 定义互相引用的两个结构时也会产生类似的问题, 可以用同样的
方法解决。

使用特权

评论回复
7
engao|  楼主 | 2017-3-31 21:58 | 只看该作者
怎样建立和理解非常复杂的声明?例如定义一个包含 N 个指向返回
指向字符的指针的函数的指针的数组?
这个问题至少有以下 3 种答案:
1. char *(*(*a[N])())();
2. 用 typedef 逐步完成声明:
typedef char *pc; /* 字符指针 */
typedef pc fpc(); /* 返回字符指针的函数 */
typedef fpc *pfpc; /* 上面函数的指针 */
typedef pfpc fpfpc(); /* 返回函数指针的函数 */
typedef fpfpc *pfpfpc; /* 上面函数的指针 */
pfpfpc a[N]; /* 上面指针的数组 */
3. 使用 cdecl 程序, 它可以把英文翻译成 C 或者把 C 翻译成英文:
cdecl> declare a as array of pointer to function returning
pointer to function returning pointer to char
char *(*(*a[])())()
通过类型转换, cdecl 也可以用于解释复杂的声明, 指出参数应该进入哪一对
括号 (如同在上述的复杂函数定义中)

使用特权

评论回复
8
engao|  楼主 | 2017-3-31 22:00 | 只看该作者
函数只定义了一次, 调用了一次, 但编译器提示非法重定义了。
在范围内没有声明就调用 (可能是第一次调用在函数的定义之前) 的函数被认
为返回整型 (int) (且没有任何参数类型信息), 如果函数在后边声明或定义成其它
类型就会导致矛盾。所有函数 (非整型函数一定要) 必须在调用之前声明。
另一个可能的原因是该函数与某个头文件中声明的另一个函数同名

使用特权

评论回复
9
engao|  楼主 | 2017-3-31 22:00 | 只看该作者
对于没有初始化的变量的初始值可以作怎样的假定?如果一个全局
变量初始值为 “零”, 它可否作为空指针或浮点零?
具有 “静态” 生存期的未初始化变量 (即, 在函数外声明的变量和有静态存储
类型的变量) 可以确保初始值为零, 就像程序员键入了 “=0” 一样。因此, 这些变
量如果是指针会被初始化为正确的空指针, 如果是浮点数会被初始化为 0.0 。
具有 “自动” 生存期的变量 (即, 没有静态存储类型的局部变量) 如果没有显示
地初始化, 则包含的是垃圾内容。对垃圾内容不能作任何有用的假设。
这些规则也适用于数组和结构 (称为 “聚合体” ); 对于初始化来说, 数组和结
构都被认为是 “变量”。
用 malloc() 和 realloc() 动态分配的内存也可能包含垃圾数据, 因此必须由调
用者正确地初始化。用 calloc() 获得的内存为全零, 但这对指针和浮点值不一定有
用。

使用特权

评论回复
10
engao|  楼主 | 2017-3-31 22:01 | 只看该作者
代码 int f() { char a[] = "Hello, world!";} 不能编译。
可能你使用的是 ANSI 之前的编译器, 还不支持 “自动聚集”(automatic aggregates, 即非静态局部数组、结构和联合) 的初始化

使用特权

评论回复
11
engao|  楼主 | 2017-3-31 22:04 | 只看该作者
这样的初始化有什么问题?char *p = malloc(10); 编译器提示
“非法初始式” 云云。
这个声明是静态或非局部变量吗?函数调用只能出现在自动变量 (即局部非
静态变量) 的初始式中。

使用特权

评论回复
12
engao|  楼主 | 2017-3-31 22:05 | 只看该作者
以下的初始化有什么区别?char a[] = "string literal"; char *p
= "string literal"; 当我向 p[i] 赋值的时候, 我的程序崩溃了。
字符串常量有两种稍有区别的用法。用作数组初始值 (如同在 char a[] 的声明
中), 它指明该数组中字符的初始值。其它情况下, 它会转化为一个无名的静态字
符数组, 可能会存储在只读内存中, 这就是造成它不一定能被修改。在表达式环境
中, 数组通常被立即转化为一个指针 (参见第 6 章), 因此第二个声明把 p 初始化成
指向无名数组的第一个元素。
为了编译旧代码, 有的编译器有一个控制字符串是否可写的开关。

使用特权

评论回复
13
engao|  楼主 | 2017-3-31 22:06 | 只看该作者
我总算弄清除函数指针的声明方法了, 但怎样才能初始化呢?
用下面这样的代码
extern int func();
int (*fp)() = func;
当一个函数名出现在这样的表达式中时, 它就会 “蜕变” 成一个指针 (即, 隐式
地取出了它的地址), 这有点类似数组名的行为。
通常函数的显示声明需要事先知道 (也许在一个头文件中)。因为此处并没有
隐式的外部函数声明 (初始式中函数名并非一个函数调用的一部分)

使用特权

评论回复
14
engao|  楼主 | 2017-3-31 22:07 | 只看该作者
声明 struct x1 { . . . }; 和 typedef struct { . . . } x2; 有什么不
同?
第一种形式声明了一个 “结构标签”; 第二种声明了一个 “类型定义”。主要的
区别是在后文中你需要用 “struct x1” 引用第一种, 而用 “x2” 引用第二种。也就是
说, 第二种声明更像一种抽象类新 —– 用户不必知道它是一个结构, 而在声明它的
实例时也不需要使用 struct 关键字

使用特权

评论回复
15
engao|  楼主 | 2017-3-31 22:08 | 只看该作者
在 C 语言中实现抽象数据类型什么方法最好?
让客户使用指向没有公开定义 (也许还隐藏在类型定义后边) 的结构类型的指
针是一个好办法。只要不访问结构成员, 声明和使用 “匿名” 结构指针 (不完全结
构类型指针)是合法的。这也是使用抽象数据类型的原因

使用特权

评论回复
16
engao|  楼主 | 2017-3-31 22:09 | 只看该作者
在 C 中是否有模拟继承等面向对象程序设计特性的好方法?
把函数指针直接加入到结构中就可以实现简单的 “方法”。你可以使用各种不
雅而暴力的方法来实现继承, 例如通过预处理器或含有 “基类” 的结构作为开始的
子集, 但这些方法都不完美。很明显, 也没有运算符的重载和覆盖 (例如, “导出类”
中的 “方法”), 那些必须人工去做。
显然的, 如果你需要 “真” 的面向对象的程序设计, 你需要使用一个支持这些
特性的语言, 例如 C++

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

34

主题

400

帖子

0

粉丝