电子大神的日记本,供应链专家的功夫茶盘,在这里记录、分享与共鸣。

登录以开始

C语言常见问题集(学习笔记)

问题1:

臭名昭著的空指针到底是什么?

答案:

语言定义中说明, 每一种指针类型都有一个特殊值 --- 空指针" ---  它与同类型的其它所有指针值都不相同, 它 与任何对象或函数的指针值都不相等"。也就是说, 取地址操作符 & 永远也不能得到空指针, 同样对 malloc() 的成功调用也不会返回空指针, 如果失败, malloc() 的确返回空指针, 这是空指针的典型用法: 表示 未分配" 或者 尚未指向任何地方" 的指针。 空指针在概念上不同于未初始化的指针。

空指针可以确保不指向任何对象或函数; 而未初始化指针则可能指向任何地方。

如上文所述, 每种指针类型都有一个空指针, 而不同类型的空指针的内部表示可能不尽相同。尽管程序员不必知道内部值, 但编译器必须时刻明确需要那种空指针, 以便在需要的时候加以区分

问题2:

怎样在程序里获得一个空指针?

答案:

根据语言定义, 在指针上下文中的常数 0 会在编译时转换为空指针。也就是说, 在初始化、赋值或比较的时候, 如果一边是指针类型的值或表达式, 编译器可以确定另一边的常数 0 为空指针并生成正确的空指针值。因此下边的代码段完全合法:

    char *p = 0;

    if(p != 0)

然而, 传入函数的参数不一定被当作指针环境, 因而编译器可能不能识别未加修饰的 0 ``表示" 指针。在函数调用的上下文中生成空指针需要明确的类型转换, 强制把 0 看作指针。例如, Unix 系统调用 execl 接受变长的以空指针结束的字符指针参数。它应该如下正确调用:

    execl("/bin/sh", "sh", "-c", "date", (char *)0);

如果省略最后一个参数的 (char *) 转换, 则编译器无从知道这是一个空指针, 从而当作一个 0 传入。(注意很多 Unix 手册在这个例子上都弄错了。)

如果范围内有函数原型, 则参数传递变为 ``赋值上下文", 从而可以安全省略多数类型转换, 因为原型告知编译器需要指针, 使之把未加修饰的 0  正确转换为适当的指针。函数原型不能为变长参数列表中的可变参数提供类型。

在函数调用时对所有的空指针进行类型转换可能是预防可变参数和无原型函数出问题的最安全的办法。

摘要:

 

 可以使用未加修饰的 0:
 需要显示的类型转换:

 初始化

函数调用, 作用域内无原型

 赋值

函数调用, 作用域内无原型

 比较
 

 固定参数的函数调用且在作用域内有原型
 

 

问题3:

用缩写的指针比较 ``if(p)" 检查空指针是否可靠?如果空指针的内部表达不是 0 会怎么样?

答案:

当 C 在表达式中要求布尔值时, 如果表达式等于 0 则认为该值为假, 否则为真。换言之, 只要写出  if(expr),无论 ``expr" 是任何表达式, 编译器本质上都会把它当 if((expr) != 0)处理。

如果用指针 p 代替 ``expr" 则 if(p) 等价于 if(p != 0)。

而这是一个比较上下文, 因此编译器可以看出 0 实际上是一个空指针常数, 并使用正确的空指针值。这里没有任何欺骗; 编译器就是这样工作的, 并为、二者生成完全一样的代码。空指针的内部表达无关紧要。

布尔否操作符 ! 可如下描述:

!expr

本质上等价于

(expr)?0:1

 

或等价于

((expr) == 0)

从而得出结论

if(!p)

等价于

if(p == 0)

类似 if(p) 这样的 ``缩写", 尽管完全合法, 但被一些人认为是不好的风格 。

问题4:

NULL 是什么, 它是怎么定义的?

答案:

作为一种风格, 很多人不愿意在程序中到处出现未加修饰的 0。因此定义了预处理宏  NULL (在 <stdio.h> 和其它几个头文件中) 为空指针常数, 通常是 0 或者 ((void *)0)。希望区别整数 0 和空指针 0  的人可以在需要空指针的地方使用 NULL。

使用 NULL 只是一种风格习惯; 预处理器把所有的 NULL 都还原回 0, 而编译还是依照 上文的描述处理指针上下文的 0。特别是, 在函数调用的参数里, NULL 之前 (正如在 0  之前) 的类型转换还是需要。

NULL 只能用作指针常数;

问题5:

在使用非全零作为空指针内部表达的机器上, NULL 是如何定义的?

答案:

跟其它机器一样: 定义为 0 。

当程序员请求一个空指针时, 无论写 0" 还是 NULL", 都是有编译器来生成适合机器的空指针的二进制表达形式。因此, 在空指针的内部表达不为 0 的机器上定义 NULL 为 0 跟在其它机器上一样合法:编译器在指针上下文看到的未加修饰的 0 都会被生成正确的空指针。

博主
wstrom@163.com
wstrom's Blog
wstrom
点击跳转